Testing ActiveScaffold

I’m currently neck-deep in a new project using Rails 3.2 and Active Scaffold.  If you’re unfamiliar with Active Scaffold, it’s a great plugin that takes care of most of your application’s CRUD functionality.  If you’re working on a data-heavy application, you should check it out.

Anyway, as great as Active Scaffold is, it certainly isn’t perfect.  The controller_generator script generates functional tests for you.  Nice, except that several of them don’t work out of the box — primarily the tests for creating and updating records.

For example, here is the create record test that was automagically generated for one of my controllers:

  test "should create student" do
    assert_difference('Student.count') do
      post :create, student: {
          age: @student.age,
          first_name: @student.first_name,
          gender: @student.gender,
          grade_level: @student.grade_level,
          last_name: @student.last_name }
    end
    assert_redirected_to student_path(assigns(:student))
  end

Nothing obviously amiss, right? Wrong.  Running the functional tests gets you the following error:

Error: test_should_create_student(StudentsControllerTest)
  NoMethodError: undefined method `each' for nil:NilClass

A little bit of troubleshooting reveals that the error is coming from deep within the bowels of Active Scaffold.  There was a time when I would have just jumped in and started hacking third party code, assuming that I was right and the API/library I was using was wrong.  These days I’m a little older and hopefully a little wiser, so I figured I should probably at least try to make changes to my code first.

A little bit of poking around on Active Scaffold’s github site reveals that I’m not the only person experiencing this issue.

Anyway, the solution is to change the params key from your model name to record, so the line:

     post :create, student: {

becomes:

     post :create, record: {

Rerunning the tests, I get a new error:

    ActionController::RoutingError: No route matches {:action=>"show", :controller=>"students"}

After I gaped dumbly (blinking occasionally) at both the test log and my code, I noticed that Active Scaffold had generated the last line of my test incorrectly:

      assert_redirected_to student_path(assigns(:student))

It’s kind of subtle, but that should be students_path, not the singular student_path.  Making that change now allows the test to pass.

Making similar changes to the “should update student” test allows that one to pass as well:

  test "should update student" do
    put :update, id: @student, record: {
        age: @student.age,
        first_name: @student.first_name,
        gender: @student.gender,
        grade_level: @student.grade_level,
        last_name: @student.last_name }
    assert_redirected_to students_path(assigns(:student))
  end

That should do it, right?  Um, not exactly.  The real bashing-head-into-keyboard-repeatedly-error has yet to be dealt with.  You see, Active Scaffold generated a pretty standard looking test for retrieving the index page:

  test "should get index" do
    get :index
    assert_response :success
    assert_not_nil assigns(:students)
  end

But running the test yields yet another problem:

=========================================================================
Failure:  expected to not be nil.
test_should_get_index(StudentsControllerTest)
test/functional/students_controller_test.rb:13:in `block in '
     10:   test "should get index" do
     11:     get :index, :current_user => @user
     12:     assert_response :success
  => 13:     assert_not_nil assigns(:students)
     14:   end
     15: 
     16:   test "should get new" do
=========================================================================

Say what?! C’mon… that should just work.  After all, getting the index page in a web browser works just fine.

Consider, however, what Active Scaffold is actually doing when you request the index page from a particular controller.  It doesn’t just display a list of records, it calls a :list action, which displays a list of records.  So perhaps we should test the ‘list’ functionality separately, while making sure that get :index at least renders the list template.

So, first we add a new test for :list

  test "should get list" do
    get :list
    assert_response :success
    assert_template 'list'
    assert_not_nil assigns(:page)
    assert_not_nil assigns(:records)
  end

Then, we modify the get :index test as follows:

  test "should get index" do
    get :index
    assert_response :success
    assert_template 'list'
  end

Whew! Everything passes. Time to go get some more coffee…