gbertl
gbertl

Reputation: 351

Rspec's expect change count not working

Here I'm testing the changes in current_user.messages.count after the current user sends a valid message. Here's my code:

spec

scenario 'adds to their messages', js: true do
  expect { find('#message_content').send_keys(:enter) }.to \
    change(current_user.messages, :count).by(1)
end

test.log

# ...
ConversationChannel is transmitting the subscription confirmation
ConversationChannel is streaming from conversation_channel_1
   (0.6ms)  SELECT COUNT(*) FROM "messages" WHERE "messages"."user_id" = $1  [["user_id", 1]]
ConversationChannel#send_message({"content"=>"foobar\n", "conversation_id"=>"1"})
   (0.3ms)  BEGIN
   (0.9ms)  SELECT COUNT(*) FROM "messages" WHERE "messages"."user_id" = $1  [["user_id", 1]]
  Conversation Load (1.6ms)  SELECT  "conversations".* FROM "conversations" WHERE "conversations"."id" = $1 LIMIT $2  [["id", 1], ["LIMIT", 1]]
   (0.7ms)  SELECT "users"."id" FROM "users" INNER JOIN "user_conversations" ON "users"."id" = "user_conversations"."user_id" WHERE "user_conversations"."conversation_id" = $1  [["conversation_id", 1]]
  SQL (1.0ms)  INSERT INTO "messages" ("content", "user_id", "conversation_id", "created_at", "updated_at") VALUES ($1, $2, $3, $4, $5) RETURNING "id"  [["content", "foobar\n"], ["user_id", 1], ["conversation_id", 1], ["created_at", "2018-01-29 11:27:13.095277"], ["updated_at", "2018-01-29 11:27:13.095277"]]
Finished "/cable/" [WebSocket] for 127.0.0.1 at 2018-01-29 19:27:13 +0800
ConversationChannel stopped streaming from conversation_channel_1
   (0.2ms)  BEGIN
   (58.8ms)  COMMIT
   (16.7ms)  ALTER TABLE "schema_migrations" DISABLE TRIGGER ALL;ALTER TABLE "ar_internal_metadata" DISABLE TRIGGER ALL;ALTER TABLE "conversations" DISABLE TRIGGER ALL;ALTER TABLE "messages" DISABLE TRIGGER ALL;ALTER TABLE "user_conversations" DISABLE TRIGGER ALL;ALTER TABLE "users" DISABLE TRIGGER ALL
  Rendered messages/_message.html.erb (0.6ms)
[ActionCable] Broadcasting to conversation_channel_1: {:message=>"<p>User 1: foobar\n</p>\n"}
# ...

The spec fails expected #count to have changed by 1, but was changed by 0 even though in the log shows INSERT INTO actually happen.

Upvotes: 5

Views: 2184

Answers (2)

Thomas Walpole
Thomas Walpole

Reputation: 49890

This doesn't work because you're not waiting long enough for the message addition to actually occur. send_keys returns as soon as the browser has been sent the key event, but knows nothing at all about any request/action triggered by that key press in the browser. This is why direct DB access tests are generally a bad idea in feature/system tests (which should generally just test user visible changes/interactions) and make more sense as request or controller.

That being said you could fix this by just sleeping after sending the key, but a better solution is to use one of the Capybara provided matchers (have waiting/retrying behavior) to synchronize the test.

scenario 'adds to their messages', js: true do
  expect do 
    find('#message_content').send_keys(:enter) }
     expect(page).to have_css(...) # check for whatever visible change on the page indicates the action triggered by send_keys has completed
  end.to change { current_user.reload.messages.count }.by(1)
end

Note: This test is also very simple for a feature test. It's okay to have multiple expectations in a feature test since it's really meant to test a whole user interaction with a specific feature of your app. You might want to look at combining this test with other tests of the same part of your app.

Upvotes: 1

carlj
carlj

Reputation: 956

Try to write : change{current_user.messages, :count}.by(1) with {}

Upvotes: 0

Related Questions