Using FactoryGirl to test Padrino web applications using Padrino and Sequel

This article helps you use the widespread FactoryGirl gem to test your controller code of Padrino web applications when you decided to use Sequel as your database helper library.


Choice of components

Yes, Padrino is still my favorite web framework. Looking at all the hoops that Rails developers have to jump through I don't feel so bad when I need some trickery to accomplish my goal with Padrino. I refuse to use too much magic and Padrino makes it pretty explicit how it works. Same is true for Sequel which is both automatic (it understands the database schema automatically) but at the same time explicitly. Now it was time that I started using proper test code before I made my current web application any more complex.

Learning from others

Looking at a list of open-source Padrino-based web applications I thought I could learn about how to test my code properly. But I hardly found any non-trivial tests. Few applications tested their helpers as unit tests. Some of them at least tested their models because that seemed a rather simple thing to do. But none of them taught me anything on how to check my controllers. Some developers even argue that testing controllers is a waste of time. Perhaps it is but I still want to try it and come to my own conclusions.

Testing mumbo-jumbo

If you are like me then you may not have used a lot of tests. And you may be confused about fixtures, factories, stubs and mocks. I'm not a life-long expert on the topic either but let me give a brief explanation. Or skip this if you know it already.

Why tests at all?

The idea of tests is that you not only write the required code for your application but extra code that tests the code you have written. After all we are human and the number of bugs that you add to any application grows with the application's complexity. Say you write a web application that provides a company phone book. After your application is half done you click through all the pages and try out a few links and enter some data into the forms. You wouldn't really need tests for that. But your application will require change later because your users will come up with new ideas or install new versions of the libraries (Ruby: gems) that you use. And after every non-trivial change you should check your application all over again because otherwise some parts may break that you did not expect to break. If you develop a number of tests for what you would check manually then you can just run the tests over and over again and be pretty confident that your changed haven't broken anything elsewhere.

An even more professional approach is test-driven development (TDD). Following the TDD methodology you first write a test that would check if your newly implemented feature works. Of course your feature does not even exist so the test fails (which is what it must do). Then you implement the actual feature and in the end see that your test succeeds. And then you can start refactoring (optimizing) your code. As long as the test succeeds you can be quite sure that you are doing well. If you intend to use tests at all then I think that TDD is the best way to proceed. You spend time writing the tests anyway and this way you can think of what your new piece of code should do.

Fixtures

Let's take the imaginary company phone book web application again. Say you want to write tests that make sure you get a list of contacts, that you can enter new contacs, delete some of them and edit them. Testing an application that stores its data in a database requires that you have some data to play with. If you have no contacts in your database then how will the list of contacs look? Will you test for the string "Sorry – there are no contacts yet."? Boring.

So fixtures are basically sample data that are loaded into the database to run tests on. Ruby-on-Rails used to work with fixtures a lot. You could add a CSV file and Rails would load that data into the database before your test code would run. That way you may already have 100 contacts in your database and can test if your application can properly search, edit and delete the data. The tests can even alter the data – no problem – at the end of the tests the database is wiped again anyway. There can even be different fixtures to test different scenarios of your application.

Fixtures are pretty helpful. But their major disadvantage is that they are static. If you add a new field to a database table then you need to edit all your fixtures and add that field there, too. Depending on the complexity of your database schema that can cost quite some hair.

An example fixture might look like this:

id name phone
1 John Doe 555-2185
2 Jane Daisy 555-2967
3 Jim Dorian 555-1957

Factories

This is nowadays the recommended way to deal with data in tests. I just said that fixtures have a drawback in that they are static and require constant editing if your database schema changes. That is exactly what factories want to avoid. A factory is code that creates (database) objects. You could write a factory for the contacts like this:

factory :contact do
  sequence(:id) { |n| n }
  name "John Doe"
  phone "555-xxxx"
end

This factory allows you to create new contacts. To create the same data as the fixtures above your could would look like:

@contact1 = create(:contact, phone: "555-2185")
@contact2 = create(:contact, name: "Jane Daisy", phone: "555-2967")
@contact3 = create(:contact, name: "Jim Dorian", phone: "555-1957")

The "create" call each created a new database row/object and saved it into the database. This is already simpler than the fixtures above because if you have to add a field then you just edit the factory and are done with it.

Now in your tests it usually doesn't matter if a contact is called "John Doe" or "Jane Daisy". So you could as well create three contacts that are all called "John Doe" with the same phone number like this:

@contacts = create_list(:contact)

Yes, that's essentially everything you need to write. And suddenly it becomes easy to create sample data. You don't even create all that sample data once and then run all tests over it. Instead you just create the minimal data that you need for any of your test cases.

At first I was a bit confused whether factories actually create rows in the database. I learned that it depends. The FactoryGirl library that I will present further down distinguishes between the ".build" and ".create" methods. "build" just creates an object im memory that you can use. "create" creates an object (with "build") and then calls ".save" on it to store it in the database. Obviously "build" is faster because things just happen in memory and not in the database. But if you want to test your Padrino controllers you surely need to have actual database rows so you must use "create".

Stubs/Mocks

A further abstraction are stubs/mocks. (I believe there is even a dinstinction but I don't use any of them seriously so I didn't delve into it any more.) They create objects that provide the absolute minimum functionality (in means of object methods) to help with your tests. My criticism is that mocks/stubs are articial objects that are not even related to your database rows/objects any more. So you are using something completely distinct to test your actual application.

In my opinion stubs are not useful in the context of testing database-driven web applications. I just use them in very rare cases where I e.g.load data from a URL on the internet for my tests. The stub then just pretends to load the data from the internet but instead loads the data locally.

Adding FactoryGirl

Are you still with me? Great. Sorry for the lengthy introduction but I never found a good short explanation of the why's of tests, fixtures and factories. Everybody seemed to assume that the topic is so crystal clear that it would not deserve further explanation. I didn't think so.

So now that you know what the purpose of factories are let's pimp your Padrino web application project with FactoryGirl – which is the most widely used library for factories in Ruby. First add the gem to your project's "Gemfile":

group :test do
  gem 'factory_girl'
end

Run "bundle" to install the gem.

I assume you are using "rspec" as a testing component here. And that you use some way to clear your test database before you run the tests. I have written another article on that topic that I recommend reading. So please edit your spec/spec_helper.rb file of your project. After the "RSpec.configure" block add this code (which is also described in the FactoryGirl documentation):

# Init FactoryGirl
FactoryGirl.definition_file_paths = [
    File.join(Padrino.root, 'spec', 'factories')
]
FactoryGirl.find_definitions

Now when you are running your rspec tests it will first read all files in the spec/factories directory and use them as factories. So you need to "mkdir spec/factories".

Adding factories

For any database model that you use you should now create a factory. This is the actual code you would need for your company phone book application:

FactoryGirl.define do

factory :contact do
  sequence(:id) { |n| n }
  name "John Doe"
  phone "555-xxxx"
end

See the FactoryGirl introduction to understand how such a factory is supposed to be coded.

The save! caveat

There is a slight catch when using FactoryGirl. Originally it was written for Rails applications that use ActiveRecord as a database helper library. When you run FactoryGirl.create then in the background an object is created using FactoryGirl.build and then saved using "save!". ActiveRecord objects have a .save! method – but Sequel objects don't. Fortunately this is easy to fix by aliasing ".save!" to just ".save".

For that workaround just create a file models/factorygirl-sequel.rb and make it contain:

class Sequel::Model
  alias_method :save!, :save
end

Simple as that.

Trying out the new factory

If you want to try it then run a Padrino console in the context of testing:

$> padrino c -e test

=> Loading test console (Padrino v.0.12.3)
=> Loading Application YourProject::App
2.0.0p247 :001 > FactoryGirl.find_definitions
 => ["/home/johndoe/projects/yourproject/factories", "/home/johndoe/projects/yourproject/test/factories", "/home/johndoe/projects/yourproject/spec/factories"]
2.0.0p247 :002 > FactoryGirl.create(:contact)
 => #<Contact @values={:id=>1, :name=>"John Doe", :phone=>"555-xxxx"}>
2.0.0p247 :003 >

As you see FactoryGirl has created a new "Contact" object, initialized it with some default data (that you defined in the factory) and saved the object to the database. (If you are using database cleaning code as recommended then this contact will get removed after you quit the Padrino console.)

Using the factory in a controller test

Recent versions of Padrino automatically add testing code for every controller that you create using "padrino gen controller…". So if you have a controller called "app/controllers/contact.rb" then Padrino has at the same time created a test file at "spec/app/controllers/contact_controller_spec.rb". Make that test file looks like this:

require 'spec_helper'

RSpec.describe "/contacts page" do
  # Get the list of contacts and make sure it loads
  describe "List of contacts"
    before do
      @contact = FactoryGirl.create(:contact)
      get "/contacts"
    end

    it "loads correctly" do
      expect(last_response).to be_ok
    end
  end

  # Create a sample contact and search for it
  describe "Search in contacts" do
    before do
      @contact = FactoryGirl.create(:contact)
      get "/contacts?search=#{@contact.name}"
    end

    it "loads correctly" do
      expect(last_response).to be_ok
    end

    it "returns: body contains the search result" do
      expect(last_response.body).to include('John Doe')
    end
  end
end

This is just a simple example that tests your /contacts and /contacts?search=… controller actions. You can see that a sample contact called "@contact" is created and saved to the database. And you can refer to the data in that @contact object later (@contact.name).

Associations

In any real-life application you will surely use associations/relationships like one-to-many or many-to-many. For example one of your contacts could have multiple phone numbers that are stored in "PhoneNumber" objects. It took me an hour to understand how to create such a sample contact with a number of phone numbers using factories. This is essentially how the factory code should look:

factory :contact do
  sequence(:id) { |n| n }
  name "John Doe"
  phone "555-xxxx"

  trait :with_phonenumbers do
    after :create do |c|
      create_list :phonenumber, 5, contact: c
    end
  end
end

The concept of "traits" in FactoryGirl is basically "additional data". So you can create a simple "Contact" object. But if you use this trait then after the contact was created the factory will create 5 more phone numbers and assign them to the contact. Of course this requires that you defined the one-to-many relationship in your model. Creating such a contact with related phone numbers is as simple as:

@contact = FactoryGirl.create(:contact, :with_phonenumbers)

Debugging

Running your tests just means executing "rake spec" on the shell. But often the output is very long and doesn't point you to the actual problem. So debugging helps. I'm using Ruby 2.0 so my approach is to use the "byebug" gem. Older versions of Ruby have different debugging modules – I'm not dealing with that right now. To add the byebug gem once again editor your "Gemfile" and add:

group :test do
  gem 'byebug'
end

And run "bundle".

Now whenever you add the line "byebug" somewhere into your tests then "rake spec" will stop at that line and you can use the debugger to examine local variables and step forward.

Conclusion

So if you want to test your controllers with minimal effort then consider using FactoryGirl as shown above. Once you wrote a few factories your tests will become very easy to write.

I also recommend the betterspecs.org web site that gives real-life examples on how to improve your tests. They also have a good chapter on factories.

What is your experience with testing and Padrino? Drop a comment.

Leave a Reply

Your email address will not be published. Required fields are marked *

By continuing to use the site, you agree to the use of cookies. more information

The cookie settings on this website are set to "allow cookies" to give you the best browsing experience possible. If you continue to use this website without changing your cookie settings or you click "Accept" below then you are consenting to this.

Close