Reputation: 35349
I wrote this to test my controller's create action which uses a nested resources. I have an Account model with a has_many :users
association. Upon sign up, an Account with a single user gets created.
describe "POST #create", focus: true do
let(:account) { mock_model(Account).as_null_object }
before do
Account.stub(:new).and_return(account)
end
it "creates a new account object" do
account_attributes = FactoryGirl.attributes_for(:account)
user_attributes = FactoryGirl.attributes_for(:user)
account_attributes[:users] = user_attributes
Account.should_receive(:new).with(account_attributes).and_return(account)
post :create, account: account_attributes
end
end
This is the failure output I'm getting; notice the difference between expected and got: it expected a symbol while it got a string.
1) AccountsController POST #create creates a new account object
Failure/Error: Account.should_receive(:new).with(account_attributes).and_return(account)
<Account(id: integer, title: string, subdomain: string, created_at: datetime, updated_at: datetime) (class)> received :new with unexpected arguments
# notice that expected has symbols while the other users strings...
expected: ({:title=>"ACME Corp", :subdomain=>"acme1", :users=>{ ... }})
got: ({"title"=>"ACME Corp", "subdomain"=>"acme1", "users"=>{ ... }})
# ./spec/controllers/accounts_controller_spec.rb:34:in `block (3 levels) in <top (required)>'
I can't help but notice that this code also smells a little. I don't know if I am going about this right. I'm new to RSpec, so bonus points if you can provide some feedback on my effort.
Upvotes: 0
Views: 468
Reputation: 29941
The params
hash generally contains keys that are strings instead of symbols. While we do access them using symbols, it is due to the fact that it is a Hash with indifferent access, which doesn't care whether it is accessed using strings or symbols.
For your test to pass, you can use the stringify_keys
method on the account_attributes
hash when setting the expectation. Then, when Rspec compares the hashes both will be string-keyed.
Now, about the review you asked: instantiating an account is really an expectation that you have upon your controller? Your tests will be less brittle if you place your assertions/expectations upon more concrete, externally-visible behaviors, instead of upon each method that your object should use.
Rails controllers are generally brittle to test, because there are many equivalent ways to manipulate ActiveRecord models... I generally try to make my controllers as dumb as possible, and them I don't unit test them, leaving their behavior to be covered by higher-level integration tests.
Upvotes: 3