1

All of my tests consistently pass when simply running rspec. I've installed Guard and now I get inconsistencies. Specifically, if I make a change to a controller, its spec might get 1 failure, or 3, or 6. They seem to be rooted in a database lock that causes the other tests to use inconsistent data.

I've tried to find similar problems but all I'm coming up with are rspec and sqlite solutions. I'm confident it is a Guard config problem since, as I said, the tests run from command line.

Here's an example of when I made a couple changes in the same file and got different results each time:

]2;[RSpec results] Failed
10:20:05 - INFO - Running: spec/controllers/V1/players_controller_spec.rb
..
  An error occurred in an after hook
    ActiveRecord::StatementInvalid: SQLite3::IOException: disk I/O error: DELETE FROM "player_connections"
    occurred at /Users/joe/.rvm/gems/ruby-2.1.1/gems/sqlite3-1.3.9/lib/sqlite3/statement.rb:108:in `step'

F...

Failures:

  1) V1::PlayersController GET players with failing auth fails when incorrect token is passed
     Failure/Error: FactoryGirl.create :client, name: "joe", auth_token: "bad token!!"
     ActiveRecord::StatementInvalid:
       SQLite3::SQLException: cannot rollback - no transaction is active: rollback transaction
     # ./spec/controllers/V1/players_controller_spec.rb:14:in `block (3 levels) in <top (required)>'

Finished in 1.26 seconds (files took 4.33 seconds to load)
6 examples, 1 failure

Failed examples:

rspec ./spec/controllers/V1/players_controller_spec.rb:13 # V1::PlayersController GET players with failing auth fails when incorrect token is passed

Randomized with seed 45348

]2;[RSpec results] Failed
10:24:38 - INFO - Running: spec/controllers/V1/players_controller_spec.rb
FF..F.

Failures:

  1) V1::PlayersController POST player should default score to 0
     Failure/Error: post :create, format: :json, player: FactoryGirl.attributes_for(:player)
     ActiveRecord::StatementInvalid:
       SQLite3::SQLException: cannot rollback - no transaction is active: rollback transaction
     # ./app/controllers/v1/players_controller.rb:18:in `create'
     # ./spec/controllers/V1/players_controller_spec.rb:59:in `block (3 levels) in <top (required)>'

  2) V1::PlayersController POST player should create a new player
     Failure/Error: let!(:client) { FactoryGirl.create :client, name: "joe", auth_token: "asdfasdfasdfasdfasdfasdf" }
     ActiveRecord::StatementInvalid:
       SQLite3::SQLException: cannot rollback - no transaction is active: rollback transaction
     # ./spec/controllers/V1/players_controller_spec.rb:50:in `block (3 levels) in <top (required)>'

  3) V1::PlayersController GET players with failing auth fails when no token exists in client record
     Failure/Error: FactoryGirl.create :client, name: "joe"
     ActiveRecord::StatementInvalid:
       SQLite3::SQLException: cannot rollback - no transaction is active: rollback transaction
     # ./spec/controllers/V1/players_controller_spec.rb:21:in `block (3 levels) in <top (required)>'

Finished in 0.61151 seconds (files took 3.81 seconds to load)
6 examples, 3 failures

Failed examples:

rspec ./spec/controllers/V1/players_controller_spec.rb:57 # V1::PlayersController POST player should default score to 0
rspec ./spec/controllers/V1/players_controller_spec.rb:52 # V1::PlayersController POST player should create a new player
rspec ./spec/controllers/V1/players_controller_spec.rb:20 # V1::PlayersController GET players with failing auth fails when no token exists in client record

Randomized with seed 5752

]2;[RSpec results] 6 examples, 3 failures in 0.6115 seconds
ain)> 

I think my Guardfile is just the example that comes when you init guard:

require 'active_support/inflector'

# A sample Guardfile
# More info at https://github.com/guard/guard#readme

# Note: The cmd option is now required due to the increasing number of ways
#       rspec may be run, below are examples of the most common uses.
#  * bundler: 'bundle exec rspec'
#  * bundler binstubs: 'bin/rspec'
#  * spring: 'bin/rsspec' (This will use spring if running and you have
#                          installed the spring binstubs per the docs)
#  * zeus: 'zeus rspec' (requires the server to be started separetly)
#  * 'just' rspec: 'rspec'
guard :rspec, cmd: 'bundle exec rspec' do
  watch(%r{^spec/.+_spec\.rb$})
  watch(%r{^lib/(.+)\.rb$})     { |m| "spec/lib/#{m[1]}_spec.rb" }
  watch('spec/spec_helper.rb')  { "spec" }

  # Rails example
  watch(%r{^app/(.+)\.rb$})                           { |m| "spec/#{m[1]}_spec.rb" }
  watch(%r{^app/(.*)(\.erb|\.haml|\.slim)$})          { |m| "spec/#{m[1]}#{m[2]}_spec.rb" }
  watch(%r{^app/controllers/(.+)_(controller)\.rb$})  { |m| ["spec/routing/#{m[1]}_routing_spec.rb", "spec/#{m[2]}s/#{m[1]}_#{m[2]}_spec.rb", "spec/acceptance/#{m[1]}_spec.rb"] }
  watch(%r{^spec/support/(.+)\.rb$})                  { "spec" }
  watch('config/routes.rb')                           { "spec/routing" }
  watch('app/controllers/application_controller.rb')  { "spec/controllers" }
  watch('spec/rails_helper.rb')                       { "spec" }

  # Capybara features specs
  watch(%r{^app/views/(.+)/.*\.(erb|haml|slim)$})     { |m| "spec/features/#{m[1]}_spec.rb" }

  # Turnip features and steps
  watch(%r{^spec/acceptance/(.+)\.feature$})
  watch(%r{^spec/acceptance/steps/(.+)_steps\.rb$})   { |m| Dir[File.join("**/#{m[1]}.feature")][0] || 'spec/acceptance' }
end

Including spec file:

describe V1::PlayersController, type: :controller do

  before :each do
    controller.request.env['HTTP_AUTHORIZATION'] = ActionController::HttpAuthentication::Token.encode_credentials('asdfasdfasdfasdfasdfasdf')
  end

  after :each do
    ActiveRecord::Base.subclasses.each(&:delete_all)
  end

  describe "GET players with failing auth" do

    it 'fails when incorrect token is passed' do
      FactoryGirl.create :client, name: "joe", auth_token: "bad token!!"

      get :index, format: :json
      expect(response).not_to be_successful
    end

    it 'fails when no token exists in client record' do
      FactoryGirl.create :client, name: "joe"

      get :index, format: :json
      expect(response).not_to be_successful
    end

  end
  describe "GET players with proper auth" do

    let!(:player_before_transact_points) { FactoryGirl.create :player, name: "Sam", default_pull_rate: 105 }
    let!(:player_2) { FactoryGirl.create :player, name: "Ma", default_pull_rate: 125 }
    let!(:client) { FactoryGirl.create :client, name: "joe", auth_token: "asdfasdfasdfasdfasdfasdf" }

    it "is authenticated" do

      get :index, format: :json
      expect(response).to be_successful
    end

    it "returns all players" do

      get :index, format: :json
      body = JSON.parse(response.body)
      power_up_names = body.map { |m| m["name"] }
      expect(power_up_names).to match_array(["Sam", "Ma"])
    end
  end

  describe "POST player" do
    let!(:client) { FactoryGirl.create :client, name: "joe", auth_token: "asdfasdfasdfasdfasdfasdf" }

    it "should create a new player" do

      expect { post :create, format: :json, player: FactoryGirl.attributes_for(:player) }.to change(V1::Player, :count).by(1)
    end

    it "should default score to 0" do

      post :create, format: :json, player: FactoryGirl.attributes_for(:player)
      expect(V1::Player.last.score).not_to be_nil
      expect(V1::Player.last.score).to eq 0
    end
  end

end
Joe Essey
  • 3,457
  • 8
  • 40
  • 69
  • Try run test in random order with `rspec --order random` I think all your test cases have dependency on each other. That causes this error. There should no dependency between test cases. – Dipak Gupta Sep 01 '14 at 14:40
  • I may be naive, but I believe I've crafted my tests in such a way that they're not dependent. I've added them above. Explicitly running random from command line seems to be fine: `kate:dot_leech_api joe$ rspec --order random ............... Finished in 1.24 seconds (files took 2.14 seconds to load) 15 examples, 0 failures Randomized with seed 12409` – Joe Essey Sep 01 '14 at 14:44
  • I'm also observing, in a spec with only 1 test, sometimes I get `QLite3::BusyException: database is locked: INSERT INTO "player_connections" ("created_at", "from_player_id", "to_player_id", "updated_at") VALUES (?, ?, ?, ?)` and others I get `ActiveRecord::StatementInvalid: SQLite3::SQLException: cannot rollback - no transaction is active: rollback transaction` – Joe Essey Sep 01 '14 at 14:54
  • My issue also happens under controller. If I keep touching the controller file to trigger guard spec run, it would error once and pass once and then keep on alternating between the two. – lulalala Sep 30 '14 at 09:30

2 Answers2

1

I had similar problem because I didn't realize that I have two guard processes running. So make sure only one process is accessing the database.

lulalala
  • 17,572
  • 15
  • 110
  • 169
0

I created a new RAILS_ENV called 'ci' and ran Guard within it. It looks like this problem may be specific to RubyMine. See @justingordon's more detailed answer:

Is it impossible to use Guard with RubyMine?

Community
  • 1
  • 1
Joe Essey
  • 3,457
  • 8
  • 40
  • 69