Testing model validations in rspec the short and sweet way
0There seems to be a divide in the Rails/TDD community over whether you should bother testing your model validations. On the one hand, it’s silly to just retest what the Rails core team has hopefully already tested — that validations work. On the other hand, I want to make sure that my models only accept the data that I design them to accept. I also tend to use my RSpec unit tests as blueprints for building out my models, in true TDD fashion, so I start out by defining what values they should accept.
For a while I was writing lots of tests like this:
require 'spec_helper'
describe MyModel do it "should create a new instance given valid attributes" do Factory(:my_model) end describe "name" do it "should be required" do blank = Factory.build(:my_model, :name => "") blank.should_not be_valid blank.errors[:name].should include("can't be blank")
blank.name = "Foo" blank.should be_valid end it "should be longer than 1 character" do too_short = Factory.build(:my_model, :name => 'a') too_short.should_not be_valid too_short.errors[:name].should include("is too short (minimum is 2 characters)")
too_short.name = 'aa' too_short.should be_valid end it "should be shorter than 101 characters" do too_long = Factory.build(:my_model, :name => 'a' * 101) too_long.should_not be_valid too_long.errors[:name].should include("is too long (maximum is 100 characters)")
too_long.name = 'a' * 100 too_long.should be_valid end endendThe first assertion makes sure that we can create a new instance of the model given valid attributes. Once that passes we can start testing the negative assertions. The next block of code asserts that we can’t create a new instance with various invalid name attribute values, and then that we can create a new instance with valid name attribute values. Some might find this approach to be overkill, but I find it reassuring. I do not enjoy writing these bulky tests for models that have a half-dozen attributes or more, however. It leads to way too much test code to maintain. So I started poking around for alternatives. (Web development rule #1: Google it! Someone, somewhere, at some point has surely encountered this problem before you.)
A colleague pointed me in the direction of a plugin named rspec_validation_expectations, and the description seemed promising:
Instead of writing specs to verify that your models are handling validation correctly, these expectations simply check that the validation is getting declared correctly in your model. [...] Since the expectations never hit the database, they are also faster than testing the traditional way.
Unfortunately, it hadn’t been updated since January 2010 and wasn’t compatible with Rails 3. (Or at least I couldn’t get it to work with Rails 3 after adding it to my Gemspec file.) So, the search continued and I came across a couple different solutions, most notably the matchers included with Shoulda and Remarkable. But after looking closely at those options it felt like too much duplication — in other words the specs ended up looking almost exactly like the AR validations you were testing which seemed too prone to copy/paste errors.
Finally I stumbled across a year-old post that discussed the same annoyance that I was facing: managing really long tests for simple validations. And more importantly, it offered a solution – accept_values_for. This gem basically provides all of the functionality I was writing out above, but in a very compact way. So I could replace all of that really long code from above with something much easier to maintain:*
require 'spec_helper'
describe MyModel do it "should create a new instance given valid attributes" do Factory(:my_model) end describe "name" do # using the accept_values_for gem it { should_not accept_values_for(:name, nil) } it { should_not accept_values_for(:name, "a") } it { should_not accept_values_for(:name, "a" * 201) }
it { should accept_values_for(:name, "a" * 2) } it { should accept_values_for(:name, "a" * 200) } endendVery nice, very simple, and not too tedious. So, how do you test model validations?
* You can even get more compact assertions with accept_values_for using the compact form of it { should_not accept_values_for(:name, nil, "a", "a" * 201) }, I just chose to break mine out into separate assertions for each value so that the success/failure messages were more specific.
Named redirect routes in Rails 3
0I didn’t see this mentioned anywhere in the Rails Guide or in my admittedly cursory Google searches, but apparently it’s possible to create named redirect routes in Rails 3. For example:
Myapplication::Application.routes.draw do match "/facebook" => redirect("http://www.facebook.com/pages/MarkZuckerbergFanClub/12345678"), :as => 'facebook' match "/twitter" => redirect("http://twitter.com/TweetyMcTweet"), :as => 'twitter'endThen you can use those in your layouts like so:
<ul class="social-links"> <li class="facebook"><%= link_to "Like us on Facebook", facebook_path %></li> <li class="twitter"><%= link_to "Follow us on Twitter", twitter_path %></li></ul>
It Should Include Only One Of – An RSpec Matcher
0I know RSpec tests should typically be implementation/data agnostic, but I ran into a situation in a unit test where I needed to make sure that the results of a query returned only one of the two records that were created during the setup but without knowing which record that would be ahead of time. This was due to the fact that the scoped finder returned a random record. So, I wrote a little matcher that lets me assert that a given array contains only one of a set of specified elements.
# Inspired by http://blog.nicksieger.com/articles/2011/01/20/rspec-2-matcher-funRSpec::Matchers.define :include_only_one_of do |*elements| match do |container| @included = [] elements.flatten.each do |e| @included << e if container.include?(e) end @included.count == 1 end
failure_message_for_should do |container| "expected array of #{container.length} elements to include only one member.\n" + "Found #{@included.flatten.inspect}" endendYou can use it like so:
require 'include_only_one_of'
describe "SomeTest" do it "should return only one wide format promotion if available" do first_wide_promotion = Factory(:promotion, :brand => @brand, :format => Promotion::FORMATS[:wide]) second_wide_promotion = Factory(:promotion, :brand => @brand, :format => Promotion::FORMATS[:wide]) @brand.featured_promotions.should include_only_one_of(first_wide_promotion, second_wide_promotion) endendJoin (implode) an Array of String Values with Formatting
1In programming, I often need to join values together from an array. Typically this will be to do something on the backend, like join a list of integers together in a comma-delimited string for use in a SQL statement. For those times, the native join/implode functions work fine. Occasionally however, we need to finesse the resulting string to look a little more readable. I wrote a small function to do so. It’s called pretty_join()
<?phpfunction pretty_join($array, $intermediate_separator = ', ', $final_separator = ' & '){ if (!is_array($array)) return null; switch (sizeof($array)) { case 0: return null; break; case 1: return reset($array); break; case 2: return implode($final_separator, $array); break; default: $end = array_pop($array); // This second call prevents the last two elements from being reversed $end = array_pop($array) . $final_separator . $end; array_push($array, $end); return implode($intermediate_separator, $array); }}?>Given an array of 0 or more values, pretty_join() will concatenate all of the values together using a common delimiter (a comma by default) except for the last two values which will be joined by the final separator (an ampersand by default).
Examples:
<?php//pretty_join with 0 elementsecho pretty_join(array());// =>//pretty_join with 1 elementsecho pretty_join(array('Big Tires'));// => Big Tires//pretty_join with 2 elementsecho pretty_join(array('Big Tires', 'Pretty Houses'));// => Big Tires & Pretty Houses//pretty_join with 3 elementsecho pretty_join(array('Big Tires', 'Pretty Houses', 'Fat Wives'));// => Big Tires, Pretty Houses & Fat Wives?>Writing the First Cucumber Feature
0I’m currently working on my first BDD project, but after reading through the Cucumber wiki for what felt like the 100th time I was still not sure where to start. I tried generating a boilerplate feature file as the documentation suggests (even though the documentation then tries to dissuade you from taking that approach in the next paragraph), but the generated file didn’t really provide much guidance so I was back to square one. Finally I started asking for advice on the freenode #cucumber channel. It seemed like a shot in the dark, knowing that every project is different and there isn’t really a single pattern that would work for everyone. The initial responses were less than illuminating, but after a few short minutes someone offered the following advice:
msassak: auth features are not very valuable, imo msassak: you need to do them, but i would implement almost anything first msassak: err anything else cbloom: So assume every user is registered for now. Then start hiding stuff after the other features are complete? msassak: that's what i would do cbloom: OK, that makes a lot of sense. msassak: imagine you are going to present this to someone who is going to pay you for it msassak: do they want to see that users can log in, or that users can do something cool on the site? cbloom: they want to see that users can print coupons cbloom: so I'll start with that msassak: yup, exactly cbloom: so simple, yet so abstract
It seems so simple now. Now to see if that actually cures my writers block.
Asserting your domain expertise and learning to say “no” to your clients
1When talking with colleagues about work, one of my biggest gripes is finding out they couldn’t say “no” to a client and are now stuck doing shit work. Having worked as a programmer and consultant for almost 10 years, I’ve certainly swallowed enough “no”s just to get a paycheck. But as the years went on, I realized that I was really just whoring myself out for pennies on the dollar when it was all said and done. The amount of time I ended up spending trying to implement a fix for that one CEO that refused to upgrade away from IE 6 was just never worth my time, and I get no satisfaction from having to hack up my own code in a less than beautiful way to make it happen.
More recently I’ve grown perversely attracted to the idea that I can say “no” to projects and still make a living. There is an axiom in the financial markets that the deal of a lifetime comes along every day, every week, every month, etc. Saying “no” to a mediocre project leaves you open to take on the great project that comes along right behind it. But even at a smaller scale, such as an enhancement request, it feels really good to not only say “no” to a client, but also back up that response with statistics that make great business sense. It wasn’t always a comfortable thing for me though, and I think it’s a privilege gained only through experience. (more…)
Earn Extra Money from Home with Web Development
1Note: This article is aimed at people who are unfamiliar with web development, as opposed to the other articles on my website which tend to be written for a more technical audience.
Many times I’ve heard or seen someone asking about ideas for ways that they can make a little extra money from home. The majority of these requests are from women that are stay-at-home mothers. That’s worth noting only because the responses therefore tend to gravitate towards Mary Kay, Athena, Silpada, etc., and which are essentially sales jobs with small commissions. I’m not knocking those jobs. Lot’s of people have personalities that are perfect for that type of job, and they have no problem making the commitment necessary to make it worth their while. But I would wager that there are just as many people for whom those types of jobs just don’t appeal to. Considering that, I am always tempted to answer with “try your hand at web development.” The inevitable response to that has been “but, I don’t know how to program!” That is a valid concern, but it’s also not an entirely accurate one. (more…)
Find the longest common substring using PHP
0I recently found a need to find the longest common substring in an array of strings in PHP. A couple of Google searches didn’t return any relevant solutions, so I decided to roll my own. I haven’t benchmarked this yet for large strings and/or arrays, but it does what I needed it to for my own purpose.
<?phpfunction longest_common_substring($words){ $words = array_map('strtolower', array_map('trim', $words)); $sort_by_strlen = create_function('$a, $b', 'if (strlen($a) == strlen($b)) { return strcmp($a, $b); } return (strlen($a) < strlen($b)) ? -1 : 1;'); usort($words, $sort_by_strlen); // We have to assume that each string has something in common with the first // string (post sort), we just need to figure out what the longest common // string is. If any string DOES NOT have something in common with the first // string, return false. $longest_common_substring = array(); $shortest_string = str_split(array_shift($words)); while (sizeof($shortest_string)) { array_unshift($longest_common_substring, ''); foreach ($shortest_string as $ci => $char) { foreach ($words as $wi => $word) { if (!strstr($word, $longest_common_substring[0] . $char)) { // No match break 2; } // if } // foreach // we found the current char in each word, so add it to the first longest_common_substring element, // then start checking again using the next char as well $longest_common_substring[0].= $char; } // foreach // We've finished looping through the entire shortest_string. // Remove the first char and start all over. Do this until there are no more // chars to search on. array_shift($shortest_string); } // If we made it here then we've run through everything usort($longest_common_substring, $sort_by_strlen); return array_pop($longest_common_substring);}?>Example:
<?php$array = array( 'PTT757LP4', 'PTT757A', 'PCT757B', 'PCT757LP4EV');echo longest_common_substring($array);// => T757?>Training With Rebekah
0Rebekah Barnes is one-half of the Sarasota-based husband-and-wife personal training team that my wife and I have been training with since this past Fall. She’s a very enthusiastic person who’s a walking encyclopedia of health food and fitness. We’ve been extremely happy with our results while working with her and I was more than happy to help her revamp her online presence.
To the right you can see the before and after screenshot. The old site was made of static HTML files that were named based on some arbitrary index (1.html, 2.html, etc.). The formatting was inconsistent across all of the pages, as was the navigation. The footer links did not match up with the pages they were linking to. The text was a hodgepodge of fonts and sizes, without use of any semantic elements such as headers or lists. As you can see from the image the layout also did not scale very will with the center image repeating on large screens. Under the covers the HTML itself was invalid, and it appeared that FrontPage was used to assemble the pages.
The new site uses a highly readable font (Calibri when available, otherwise Arial or Helvetica) with a large base font size and line spacing. All of the elements are written with semantic HTML, with the Blueprint CSS framework assisting with layout. All of the images from the old site were re-used after being optimized, and some URL rewrite rules were put in place to redirect requests from any of the old pages that may have been book marked. jQuery is used on the Rates page to implement the tab functionality. I also setup Google Analytics and Google Webmaster Tools accounts to track page view statistics and fine tune the content for Google’s indexing bots. Behind the scenes the pages use PHP (the html extension is mime-typed to PHP on the server) to manage the common areas of the layout and process incoming form submissions. The shopping cart functionality is managed through PayPal.
Over all I’m pretty happy with the result. As far as the design goes this is pretty much as far as I could take it on my own, but Rebekah seems to be very happy with it as-is. It’s certainly an exponential improvement over what she had previously.
If you’re looking for personal fitness training in the Sarasota area, even if you’re just here for a week or two on vacation, I highly recommend Rebekah. Please give her site a read through to find out if she’s a good fit for your needs and goals.
Reboot Redux
0I started this blog back in 2004-ish on the domain csb7.com. I lost everything once due to a server crash, then lost it again when my hosting company shut down and I was stuck with a corrupt Plesk backup. At some point I may try to scour the Wayback Machine and try to resurrect some of my more popular posts from back in the day, but for now I’ll just be looking forward to newer content.



Recent Comments