Search code examples
ruby-on-railsrubyunit-testingcontrollerfixtures

ControllerTest error, Couldn't find Transaction with 'id'=


The Show action for my Controller is not passing the unit test. I'm getting an error in my ControllerTest. Here is the error I get from 'rake test':

> ERROR["test_should_get_show", TransactionsControllerTest, 2015-07-18
> 00:30:18 -0400]  test_should_get_show#TransactionsControllerTest
> (1437193818.29s) ActiveRecord::RecordNotFound:        
> ActiveRecord::RecordNotFound: Couldn't find Transaction with 'id'=
>             app/controllers/transactions_controller.rb:21:in `show'
>             test/controllers/transactions_controller_test.rb:6:in `block in <class:TransactionsControllerTest>'
>         app/controllers/transactions_controller.rb:21:in `show'
>         test/controllers/transactions_controller_test.rb:6:in `block in <class:TransactionsControllerTest>'
> 
>   5/5:
> [=======================================================================================================================] 100% Time: 00:00:00, Time: 00:00:00
> 
> Finished in 0.24993s 5 tests, 4 assertions, 0 failures, 1 errors, 0
> skips

Despite this error, the view is successfully generates the page in the browser when I use this url: http://localhost:3000/transactions/1

So, there is an error somewhere. It's not failing.

transactions_controller.rb

class TransactionsController < ApplicationController

  def new
   @transaction = Transaction.new
  end

  def create
    @transaction = Transaction.new(transaction_params)
    if @transaction.save
      # Handle a successful save.
    else
      render 'new'
    end
  end

  def index
    @transactions = Transaction.all
  end

  def show
    @transaction = Transaction.find(params[:id])      #this is row 21
  end

    private
      def transaction_params
        params.require(:transaction).permit(:company, :month, :account_code, :description, :transaction_amount)
      end
end

transaction_controller_test.rb

require 'test_helper'

class TransactionsControllerTest < ActionController::TestCase
  test "should get show" do
    transaction = Transaction.create
    get :show, id: transaction.id          #this is row 6
    assert_response :success 
  end

end

The following fixture was automatically generated by rails. I'm new at rails and have to admit that I don't really understand fixtures.

transactions.yml

one:
  company: MyString
  month: 2015-06-19
  account_code: MyString
  description: MyString
  transaction_amount: 1.5

two:
  company: MyString
  month: 2015-06-19
  account_code: MyString
  description: MyString
  transaction_amount: 1.5

In my routes file, I'm setting the root to the index action. I plan to access the show action via the url http://localhost:3000/transactions/1 and (as mentioned above) it actually works.

routes.rb

Rails.application.routes.draw do
  root            'transactions#index'
  resources       :transactions
end

transaction.rb

class Transaction < ActiveRecord::Base
    validates :month, presence: true 
    validates :account_code, presence: true
end

transaction_test.rb

require 'test_helper'

class TransactionTest < ActiveSupport::TestCase

    def setup
        #@transaction = transactions(:one)
    @transaction = Transaction.new(company: "Inc", month: Date.new(2015,6,15).to_s, 
                                    account_code: "80-32100-12-1201-60010", 
                                    description: "Salaries & Wages - Histo Gross 1", transaction_amount: 100000)
    end

    test "should be valid" do
        assert @transaction.valid?
    end

    test "month should be present" do
    @transaction.month = "     "
    assert_not @transaction.valid?
  end

    test "account_code should be present" do
    @transaction.account_code = "     "
    assert_not @transaction.valid?
  end

  test "month cannot be a string" do      #not sure if this is worth anything at all
    @transaction.month = "xxxxxxxxxxxx"
    assert_not @transaction.valid?
  end


end

The test auto generated by Rails (before I modified it) was very simple:

require 'test_helper'

class TransactionsControllerTest < ActionController::TestCase
  test "should get show" do
    get :show   
    assert_response :success 
  end

end

I modified it to the current version above because (I believe) the test should first create a transaction and then reference id somehow. But I'm not sure I did this correctly.

I've seen questions similar to this on Stackoverflow. For instance, this question was helpful, but didn't quite get me there Count; find User with id= Minitest

NOTE: Initially, I set up the Controller incorrectly. I set it up as a singular "TransactionController" and changed it to the plural "TransactionsController". So I had to change the name in the unit test also. Perhaps this has had an effect on the app.

So... questions:

  1. why is the unit test failing while the show action is actually
    working in the browser?
  2. How might I change the code in the unit test so it recognizes that I'm using transaction_params?
  3. Do I have to modify the fixtures when I make changes to the unit_test or the controller?

Solution

  • Ok, so coorasse's suggestion to use create! forced the app to uncover a validation error. The initial error (Couldn't find Transaction with 'id'= ) was replaced with a new error:

    ActiveRecord::RecordInvalid: Validation failed: Month can't be blank, Account code can't be blank

    This was confusing because it leads me to think of a model validation error. Why am I getting a model validation error in a ControllerTest? Also, the transaction.rb model clearly shows that that I've added validations to satisfy the presence of Month and AccountCode.

    So, I spent some time reading through "A Guide to Testing Rails Applications" on RailsGuides http://guides.rubyonrails.org/testing.html All the way down in Section 8, it shows how to include a block of code before each test. Here is the new transaction_controller_test

    transaction_controller_test.rb (REVISED)

    require 'test_helper'
    
    class TransactionsControllerTest < ActionController::TestCase
    
        # called before every single test
      def setup
        @transaction = transactions(:one)
      end
    
      test "should get show" do
        get :show, id: @transaction.id
        assert_response :success 
      end
    
    end
    

    After this the test passed! Unfortunately, the RailsGuides doesn't tell me why I would want to use setup in a controller test. If any smart Rails gurus has some guidance on this, your comments would be appreciated.