Default controller test "create" attributes uses existing fixture attributes

It’s always bothered me that the default create scaffold test uses the same attributes as the fixture.

The default controller test looks like this:

  test "should create hotdog" do
    assert_difference('Hotdog.count') do
      post hotdogs_url, params: { hotdog: { name: @hotdog.name, size: @hotdog.size } }
    end

    assert_redirected_to hotdog_url(Hotdog.last)
  end

When you add the uniqueness validation, you end up with:

# Running:

..F

Failure:
HotdogsControllerTest#test_should_update_hotdog [/private/tmp/papercut/test/controllers/hotdogs_controller_test.rb:38]:
Expected response to be a <3XX: redirect>, but was a <200: OK>


rails test test/controllers/hotdogs_controller_test.rb:36

.F

Failure:
HotdogsControllerTest#test_should_create_hotdog [/private/tmp/papercut/test/controllers/hotdogs_controller_test.rb:19]:
"Hotdog.count" didn't change by 1.
Expected: 3
  Actual: 2


rails test test/controllers/hotdogs_controller_test.rb:18

..

Finished in 0.345813s, 20.2422 runs/s, 23.1339 assertions/s.
7 runs, 8 assertions, 2 failures, 0 errors, 0 skips

It’s not obvious that the reason these failures are happening are because the model validations didn’t pass, particularly since we are testing the controller, not the model.

Some thoughts:

  • When scaffolding is generated, can we do something more clever with the templating? Maybe inspect the datatypes, and hardcode some values in rather than reference the fixture? Or some helper method to generate the create attributes? This has downsides too, but maybe someone can riff on that.
  • Some way to expose error messages to the test reporter?

This isn’t a wtf when you understand what’s at play, but it always got me when I was new, and I’ve seen it get other greenhorns as well. There’s nothing in the test suite that points you in the right direction.

The true papercut seems to be that there’s a controller test setup, but not a model test to help identify that your model creation doesn’t work.

Current model scaffolding is a blank canvas:

require 'test_helper'

class HotdogTest < ActiveSupport::TestCase
  # test "the truth" do
  #   assert true
  # end
end

Adding this to the scaffolding might be another option:

require 'test_helper'

class HotdogTest < ActiveSupport::TestCase
  setup do
    @hotdog = hotdogs(:one)
  end

  test 'it can be created' do
    # The same move the controller uses to create something
    assert Hotdog.create!({ name: @hotdog.name, size: @hotdog.size })
  end
end

This creates a duplication of “How does our test suite get attributes for a new obj?” but maybe that could get dried out.

This helpfully results in the error:

E

Error:
HotdogTest#test_it_can_be_created:
ActiveRecord::RecordInvalid: Validation failed: Name has already been taken
    test/models/hotdog_test.rb:13:in `block in <class:HotdogTest>'

And now it is more clear why your controller tests are also suddenly failing.