A Journey On Rails

Cleaner fixture generation and maintenance

Posted in Rails, Testing, Uncategorized by Vikram Venkatesan on February 9, 2011

Those who have used fixtures in Rails may know how handy they are when writing tests, helping us concentrate on the functionality, not having to worry about the test data. But, fixtures are not without their own problems.

  • They do not go through validations. ActiveRecord validations are not run while creating fixtures and there is no guarantee that the fixtures form valid AR objects. I’ve found many invalid fixtures sitting in my projects, yet the tests running safely based on the inconsistent data. This might very well be the case with any rails project. What was the last time you ran .valid? on your fixture records?
  • Records created through callbacks will have to be created manually. For instance, if there are UserObserver callbacks to create some records ‘on create’, they will have to be created manually. If not done, it’s a hole in the data set that is different from the one the real application uses in production/development. Hence, our tests might miss some data and cases that happen in production setup.
  • Less motivation to create fixture records manually and hence decreased test coverage. Managing YML files manually is no fun. The harder a process, the lesser will be the motivation to follow it.
  • Harder to maintain. It’s very hard to create records manually in some cases, like through tables. For instance, it took way too long for me to manually create all the intermediate table (in a habtm or has_many :through) records, which are supposed to be auto-generated by rails. Lets say, we have User has_many :cars, :through => :user_cars. Why should I create the user_car records by hand? Aren’t they supposed to be invisible to the application layer and hence the developer?
  • Hard to get a complete picture of the test data from fixtures, especially when there are more join tables, STI, etc.,.
  • Timestamps are absolute. The time values in the fixture data set are usually set to some hand-crafted time during the fixture creation. In some cases, the time may have to be closer to the time of test execution. Needless to say, it’s hard to update the time values for all the records in the fixtures when the need arises.

More than all these, fixtures do not appear to be the cleanest technique for the developers to create data at the data layer, wherein it’s real use lies in the application layer. We can very well write SQL statements for inserting fixtures, but it’s tedious and error prone. Fixtures are no different, except some support from Rails for naming records, associations, etc.,. But, they are still far from being an application layer functionality.

How about having a mechanism where we can write simple Rails code to create the fixture data? Fixture Generator plugin is just for that.

Installation

ruby script/plugin install git@github.com:venkatev/fixture_generator.git

Usage

class MyProjectFixtureGenerator < FixtureGenerator
 # Implement the 'populate' method in your populator.
 def populate
 # Create your records here and add them to the fixture set by calling
 # 'add_record(record_name, record_object)'. Please see lib/fixture_generator.rb for more details.
 end
end

# Simply invoke the following line test environment to generate fixture YAML files.
#
MyProjectFixtureGenerator.generate

# OR, even simple, run the following rake task. You may have to fix the fixture generator class name in the rake task
# tasks/fixture_generator_tasks.rake
#
rake fixtures:generate

Modifying fixture data is now lot more easier. Just go to your fixture generator ruby file, modify the record creation statements, and run rake fixtures:generate. The new YML files are right there, ready to be used in your tests! Using this approach, we may not even need to revision control the fixtures. The generator script is the single source of fixture data. The developers can run the rake task and generate the fixtures locally. Give it a try and let me know your feedback.

 
Happy testing!!!

Advertisements
Tagged with: ,

Testing Rails exception handling

Posted in Rails, Testing by Vikram Venkatesan on December 23, 2009

I recently found a good article by Neeraj on rescue_action_in_public that saved a good amount of time for me while testing/modifying the rescue behaviour in ApplicationController.

But, testing it was not so trivial. Typicaly, the test.rb will be configuring all test requests to be considered local by through config.action_controller.consider_all_requests_local = true

Now, we need to override this setting only for a few test cases. Also, local_request? must be made to return false.
The former is quite straight forward, done by adding the following line of code to your test case.

@controller.consider_all_requests_local = false

For local_request? to return false, we must set @request.remote_addr to something other than 0.0.0.0 for the test framework to consider the request to be not local.
Rails provides a cleaner way to do this. Calling rescue_action_in_public! will do the job for us. So, putting the pieces together, here is a sample test code to get started

class DummyController < ApplicationController
  # Add actions for triggering different error cases (404 - Not Found, 403 - Forbidden, etc.,). Or have a single action and stub it.

  # Some dummy action throwing an exception
  def some_action
    raise "Some error"
  end
end

class ErrorHandlingTest < ActionController::TestCase
  tests DummyController

  def setup
    super
    @controller.consider_all_requests_local = false
    rescue_action_in_public!
  end

  def test_rescue_exception
    get :some_action
    # Assert the expected behaviour as follows
    #   assert_template "#{RAILS_ROOT}/app/views/common/500.html"
  end
end

DRYing up rails tests

Posted in Rails, Testing by Vikram Venkatesan on November 12, 2009

The rails tests are probably the most repetitive and unstructured pieces of code that we write, in my opinion. We write tests for all scenarios and cases, with each one repeating the same preparation code before the actual test begins. Also, it’s always hard to find whether there is a test for a given scenario/case, no matter how disciplined we are in naming and structuring the tests. This is true for both unit and functional tests.

Cucumber is definitely a sigh of relief. But, it is a tool specifically built to aid BDD, used to write tests at the highest level of abstraction called Integration testing.  It can’t be used to DRY up the unit or functional tests. I personally feel it’s not so productive since the developers write the complete English-Ruby conversion code and there is no reuse of scenarios and structuring. If there are two scenarios differing in the last step, you will be duplicating most the statements.

Since any test case is nothing but a combination of many scenarios (be it unit or functional), what would be helpful is a way to nest the tests based on multiple scenarios. I have tried to come up with a test library that can aid in writing such tests with minimal code and in a more structured way.

The library is available as a plugin @ http://github.com/venkatev/nested_scopes