1 July 2015

web development, Rails, testing Integration testing for Rails APIs part 2: Writing less code

In this second part I’ll show you some techniques that help you spend less time writing tests. Let’s take a look at alternate ways of writing expectations and how to generate test data using factories.

If you’re new to this series, take a look at the first part before you continue. It’s all right, this post will still be here afterwards!

Writing more concise expectations

RSpec’s expressive syntax is great, but some commonly written expectations could be shorter.

Checking the status and body of a received HTTP response is a common task in integration specs:

context 'when the requested ID is invalid' do let ( :id ) { - 1 } it 'returns not found' do expect ( response . status ). to eq 404 expect ( response . body ). to match /not\s+found/ end end Using a regular expression for the body is a bit silly but works as an example. I’ll show you a really neat way to test JSON in the next part!

If you’re looking to make expectations like this shorter, rspec-its has got your back.

With it you can skip the it block completely since the behaviour is described pretty well by the expectations:

# Don't forget to set the subject after RSpec.describe subject { response } context 'when the requested ID is invalid' do let ( :id ) { - 1 } its ( :status ) { should eq 404 } its ( :body ) { should match /not\s+found/ } end Writing expectations with rspec-its.

Note that I’m using the one-liner should syntax, which is not the same as the monkey-patched Object#should that’s been deprecated. You can also use is_expected.to .

rspec-its is meant for situations where you’re testing an attribute. Note that the expectation should always make sense as a sentence so that RSpec can automatically infer the test description from the matcher.

If you think status codes aren’t readable enough, rspec-rails has a couple of matchers that should help you:

expect ( response ). to have_http_status ( :success ) # 200 expect ( response ). to have_http_status ( :not_found ) # 404 expect ( response ). to be_success # 2xx The first two aren’t shorter but they could be more readable.

I wish the status codes were similar to the content_type attribute that’s actually an object that can be equal to lots of different values. For example, its representation of application/json is also equal to :json .

Unfortunately these symbols can’t be used with rspec-its since the status codes in the response object are just Fixnum s.

Manufacturing test data with factories

Integration testing usually requires all kinds of test data.

Rails gives you YAML-based fixtures by default, but populating the database with diverse data is a bit of a chore with them since you need to fiddle with ERB templates and YAML.

Fortunately the lovely people at Thoughtbot have come up with factory_girl, a library that allows you to write factories for your models.

Let’s assume that we have a simple User model set up that takes an email address and a password.

With factory_girl you could write a factory that manufactures instances of that User model:

FactoryGirl . define do factory :user do email [email protected]' password 'hunter2' end end /spec/support/factories/user.rb

Using this factory in a spec is dead simple:

RSpec . describe 'GET /users/:id' do let ( :user ) { FactoryGirl . create ( :user ) } # or if you include the helper methods... let ( :user ) { create ( :user ) } # ... end Using a factory in a spec. Check out factory_girl’s docs how to include the helper methods.

At some point we might need to add support for multiple kinds of users, e.g. regular unprivileged users and special admin users.

We could do that by adding an enum attribute to the model called role that can be set to either 'unprivileged' or 'admin' .

factory_girl then lets us define traits to modify the base factory:

FactoryGirl . define do factory :user do email [email protected]' password 'hunter2' role 'unprivileged' trait :as_admin do role 'admin' end end end /spec/support/factories/user.rb (with traits)

You can use traits by specifying them after the factory name:

RSpec . describe 'GET /users/:id' do let ( :user ) { create ( :user ) } let ( :admin ) { create ( :user , :as_admin ) } # ... end Using traits in a spec. Here I’m using factory_girl’s helper methods.

Generating dynamic test data

It’s all well and good, but the values that we’re testing are looking a bit static, don’t you think?

Let’s improve that using the faker gem that generates fake data. It can generate almost anything you might want as a value!

You can specify a block in the factory to set the attribute dynamically:

Don’t forget about Ruby’s handy core methods such as Array#sample . It’s really useful if you want to pick a random value for an enum attribute.

FactoryGirl . define do factory :user do email { Faker :: Internet . email } password { Faker :: Internet . password } role 'user' trait :admin do role 'admin' end end end Using blocks to set dynamic values in a factory.

Now you’ll get different data for each new user created. This makes it more likely for you to be able to catch any unanticipated edge cases that might crop up with validation for example.

factory_girl has some other great features as well that are especially well suited for testing APIs but you’ll have to wait until the next part to find out more.

Any tips or thoughts to share? Leave a comment!

There are still many goodies to come. Subscribe to the newsletter below and I’ll let you know about them first.

Related posts