meatherly

Rails App Testing

Disclaimer: This is just my opinion on rails testing. It’s no way a “must follow guide for testing” Also this blog post is a work progress.

#Api Testing A good rule of thumb for writing tests for your api is: You should test every thing in your api documentation to make sure your endpoints, accepted params, and response body does what it says it does. With that being said, this makes sure your api documentation is tested and your clients can trust it. The clients depending on your api shouldn’t have to tell you when your api is broken because you’ve made changes to it. Your tests should let you know this ahead of time.

##Things you should test when writing API tests ###The serializer/response body This way you will know exactly what the serializer is returning and you can be confident when you have to make changes to your models or serializers that it won’t break the api.

At minimum you should be testing the keys. That way you know what keys you’re returning with the api.

You can write and test your response body in your controller tests or in your serializer tests.

#####Examples

If the documentation says that GET /user/:id will return:

{
   first_name: string,
   last_name: string
} Then you should at least have a test that does something along the lines of:

describe '#GET user/:id' do
   expected = {
      first_name: user.first_name,
      last_name: user.last_name,
   }.to_json
   get :show, id: 1
   response.body.should eq expected
end

If you want to test the serializer you can do something like this:

These should go in the spec/serializers folder.

describe UserSerializer do
  it "should return the right attributes for a user" do
    serializer = UserSerializer.new User.new(id: 1, first_name: 'first', last_name: 'last')
    expect(serializer.to_json).to eql('{"user":{"id":1,"first_name":"first", "last_name":"last"}}')
  end
end

The above can be harder to write and but it is more verbose. If you don’t like that option you can write controller tests and use the .all? block to test the keys of the json.

api/users_controller_spec.rb

describe Api::UsersControllers do
  let!(:user){ Fabricate :user }
  before do
    get api_users_path
  end
  it 'should have the correct attributes' do
    get :show, id: 1
    json = JSON.parse(response.body)
    expect{
      user.attributes.all? do |key|
        json.has_key(key)
      end
    }.to be
  end
end

The code above checks to make sure that all the keys for the model are represented in the response body. If you need to add more attributes you can alway just give the .all? block a hash with all the keys that should be in there. This will not only check if all your keys are there but will fail if you add or remove a key from your serializer.

good sources and gems for testing can be found here:

https://github.com/ahawkins/active_model_serializers-matchers

http://dhartweg.roon.io/rspec-testing-for-a-json-api

###Endpoints

You should check accepted params for the endpi. required and optional

These are easy test to write just because you’re just checking the model validations.

#####Examples models/user.rb

class User < ActiveRecord::Base
  validates :first_name, :last_name, presence
end

You should always tests your models validation code. This way you know that you’re validations are working properly.

#Model tests

##What should you be testing

  • Model validations

  • Instance and class methods

###Model Validations You should always start with a valid fixture.

user_fabricator.rb

  Fabricator(:user) do
    first_name { sequence(:first_name) { |n| "first#{n}" } }
    first_name { sequence(:last_name) { |n| "last#{n}" } }
  end

user_spec.rb

describe User do
     let(:user){ Fabricate.build :user }
     subject { user }
     it{ should be_valid }
end

This makes sure you have all the correct data in your fixture. It kinda tests your model validations as well but I still like to be more verbose about this. This is why I’ve found something very easy to do this as well. You can use shoulda-matchers to test your model validations. I’ll write 2 examples below, One with shoulda and one without.

Without shoulda-matchers

  describe User do
    describe 'must have first name' do
      before { subject.first_name = nil }
      it{ should be_invalid }
    end
  end

With shoulda-mathchers

  describe User do
    it { should validate_presence_of :first_name }
  end

###Instance and class methods

You should be testing you model instance and class methods as well. This is important because I’ve seen a lot of times were people make instance methods for some view code and then someone comes along and changes it. Then this breaks ever where this instance code was used and no one knows about until it goes into production. Remember tests are to protect you and others from changing something that is being used somewhere else in code base. I’ll start out with something basic for testing instance methods

user.rb

  class User < ActiveRecord::Base
    def to_s
      [first_name, last_name].join
    end
  end

user_spec.rb

  describe User do
    its(:to_s){ should eq [subject.first_name, subject.last_name].join }
  end

This project is maintained by meatherly