9

I'm struggling with this for quite a while now: I'm trying to upgrade an app from Rails 3.2 to Rails 4. While on Rails 3.2 all specs are passing, they fail under certain conditions in Rails 4.

Some specs are passing in isolation while failing when run together with other specs.

Example Video

https://www.wingolf.org/ak-internet-files/Spec_Behaviour.mp4 (4 mins)

This example video shows:

  1. Running 3 specs using :focus–––green.
  2. Running them together with another spec–––two specs passing before now fail.
  3. Running the 3 specs, but inserting two empty lines–––one spec fails.
    1. Undo does not help when using guard.
    2. focus/unfocus does not help.
    3. Restarting guard does not help.
    4. Running all specs and then running the 3 specs again does help and make them green again. But adding the other task makes two specs fail, again.

As one can see, some specs are red when run together with other specs. Even entering blank lines can make a difference.

More Observations

  • For some specs, passing or failing occurs randomly when run several times.
  • The behavior is not specific to one development machine but can be reproduced on travis.
  • To delete the database completely between the specs using database_cleaner does not help.
  • To Rails.cache.clear between the specs does not help.
  • Wrapping each spec in an ActiveRecord::Base.transaction does not help.
  • This does occur in Rails 4.0.0 as well as in Rails 4.1.1.
  • Using this minimal spec_helper.rb without spring or anything does not help.
  • Using guard vs. using bundle exec rspec some_spec.rb:123 directly doesn't make a difference.
  • This behavior goes for model specs, thus doesn't have to do anything with parallel database connections for features specs.
  • I've already tried to keep as many gems at the same version as in the (green) Rails-3.2 branch, including guard, rspec, factory_girl, etc.–––does not help.

Update: Observations Based on Comments & Answers

  • Thanks to engineerDave, I've inserted require 'pry'; binding.pry; into one of the concerning specs. Using the cd and show-source of pry, it was ingeniously easy and fun to narrow down the problem: Apparently, this has_many :through relation does not return objects when run together with other specs, even when called with (true).

    has_many(:groups,
      -> { where('dag_links.descendant_type' => 'User').uniq },
      through: :memberships,
      source: :ancestor, source_type: 'Group'
      )
    

    If I call groups directly, I get an empty result. But if I go through the memberships, the correct groups are returned:

    @user.groups                                #  => []
    @user.groups(true)                          #  => []
    @user.memberships.collect { |m| m.group }   # returns the correct groups
    

    Has Rails changed the has many through behavior in Rails 4 in a way that could be responsible? (Remember: The spec works in isolation.)

Any help, insights and experiences are appreciated. Thanks very much in advance!

Code

How to Reproduce

This is how one can check if the issue still exists:

Preparation

git clone git@github.com:fiedl/wingolfsplattform.git
cd wingolfsplattform
git checkout sf/rails4-minimal-update
bundle install
# please create `config/database.yml` to your needs.
bundle exec rake db:create db:migrate db:test:prepare

Run the specs

bundle exec rspec ./vendor/engines/your_platform/spec/models/user_group_membership_spec.rb
bundle exec rspec ./vendor/engines/your_platform/spec/models/user_group_membership_spec.rb:213

The problem still exists, if the spec :213 is green in the second call but is red when run together with the other specs in the first call.

Community
  • 1
  • 1
fiedl
  • 5,667
  • 4
  • 44
  • 57
  • Are you not cleaning up data properly between tests? – sevenseacat Jul 22 '14 at 00:43
  • @sevenseacat, I've tried rspec's [use_transactional_fixtures](https://relishapp.com/rspec/rspec-rails/docs/transactions), [database_cleaner](https://github.com/DatabaseCleaner/database_cleaner) and [wrapping each spec in a transaction manually](https://github.com/rails/rails/issues/10376). Is there a another, maybe better way? – fiedl Jul 22 '14 at 00:55
  • Are you absolutely sure your running in the test environment? – DickieBoy Jul 24 '14 at 13:34
  • @DickieBoy, yes, I am surely running in the test environment. See, for example, [this travis log](https://travis-ci.org/fiedl/wingolfsplattform/builds/30028909), line 13, where it reads `export RAILS_ENV=test`. Locally, I checked by trying `RAILS_ENV=test bundle exec rake db:drop`. – fiedl Jul 25 '14 at 14:12
  • 2
    Is the ordering of the tests fixed, or are they being run in a random order? That behavior may have changed if you upgraded the rspec version at the same time you upgraded Rails. – Peter Goldstein Aug 01 '14 at 06:56
  • 2
    Also I suspect that this odd behavior is related to your use of explicit variables (e.g. @user) in your specs. My guess would be that the variables are 'leaking' from one spec to another. Instead of using explicit variables I'd use 'let' statements, which ensure that the values can't leak between specs. – Peter Goldstein Aug 01 '14 at 06:57
  • @PeterGoldstein, thanks, but this problem also exists when keeping rspec on 2.14.1. I've just added [this minimal-gem-update branch](https://github.com/fiedl/wingolfsplattform/tree/sf/rails4-minimal-update) as proof. Thus, the spec order behavior has not changed from `master` to our rails4 branch. Also, the old explicit use of `@user` rather than `let` does not compromise the specs on our master branch in Rails 3. – fiedl Aug 05 '14 at 17:44
  • Given that you seem to be using Rails.cache, are you cleaning that out between specs? – Frederick Cheung Aug 05 '14 at 17:51
  • @FrederickCheung, yes, [I've tried this](https://github.com/fiedl/wingolfsplattform/commit/6dda7970cf883a2403af309daa3ed2eb069254cd) using `config.before(:each) { Rails.cache.clear }`. This does not resolve the issue. – fiedl Aug 05 '14 at 17:56

1 Answers1

5

Based on that you're using:

  • the should syntax
  • that you indicate you've upgraded recently (perhaps a bundle update?)
  • that your failure messages indicate a NilObject error.

Is something like this perhaps what is causing it?

https://stackoverflow.com/a/16427072/793330

Are you are calling an object in your test which hasn't been instantiated yet?

I think this might be an rspec 3 upgrade issue where should is deprecated.

Have you ruled out an rspec gem upgrade to the new rspec 3 syntax (2.99 or 3.0.0+) as the culprit?

"Remove support for deprecated expect(...).should. (Myron Marston)"

IMO this behavior would not be caused by a Rails 4 update as its centered around your test suite.

Update (with pry debug):

Also you could use the pry gem to get a window into what is going on in your specs.

Essentially you can put a big "stop" sign (similar to a debug break) right before the spec executes to get a handle on the environment at that point.

it {
  require 'pry'; binding.pry
  should == something
}

Although beaware sometimes these pry calls wreck havoc on guard's threading and you have to kill it with CTRL+Z and then kill -9 PID that shows.

Update #2: Looking at updated answer.

You might be running up against FactoryGirl issues based on your has_many issue

You may need to trigger a before action in your Factory to pre-populate the associated record. Although this could get messy, i.e. here be monsters, you can trigger after and before callbacks in your factory that will bring these objects into being.

after(:create) do |instance|
    do stuff here
end
Community
  • 1
  • 1
engineerDave
  • 3,887
  • 26
  • 28
  • Thanks, but I'm not sure if this applies, because I've already tried to keep as many gems at the same version as in the (green) Rails-3.2 branch, including guard, rspec, factory_girl, etc. The problem still occurs then. I'm going to prepare another branch with minimal gem updates as proof. – fiedl Aug 05 '14 at 15:40
  • If you have a look at [this diff](https://github.com/fiedl/wingolfsplattform/compare/sf/rails4-minimal-update) or type `git diff master..sf/rails4-minimal-update Gemfile.lock |grep rspec` locally, you can see that rspec has not been updated and is still fixed at `rspec (2.14.1)`. [This is the minimal-gem-update branch](https://github.com/fiedl/wingolfsplattform/tree/sf/rails4-minimal-update). – fiedl Aug 05 '14 at 17:37
  • Are you using rvm? you may need to rvm gemset empty and make sure there isn't a ./bin directory being picked up with a different version of rspec being used. Also put a pry call into the spec "require 'pry'; binding.pry;" right before the failed spec and see if the object is present or look at the state of the database at that point – engineerDave Aug 05 '14 at 21:33
  • we using neither rvm nor bin stubs. We are always going through `bundle exec`. No `bin` directory exists. Since we are using `rbenv`, I've double checked: `rbenv rehash` does not solve the problem. I'm going to try your `pry` suggestion in a minute. – fiedl Aug 05 '14 at 22:41
  • thanks for the hint inserting `require 'pry'; binding.pry;`. When running the spec in isolation, the object is present, when running together with the other specs, `subject` is `nil`. Also, entering the subject definition returns `nil`. But I didn't know the pry trick before. Maybe I'll find something by debugging with pry. – fiedl Aug 05 '14 at 23:17
  • Using the [`cd` and `show-source` of `pry`](https://github.com/pry/pry/wiki/Source-browsing), it was ingeniously easy and fun to narrow down the problem. Thanks! Apparently, [this **has_many through relation does not return objects**](http://github.com/fiedl/wingolfsplattform/blob/f8efddf251c678e809eacc70c0e8d61d10a6368d/vendor/engines/your_platform/app/models/user_mixins/memberships.rb#L52) when run together with other specs, even when called with `(true)`. Has Rails changed the has many through behavior in Rails 4 in a way that could be responsible? (Remember: The spec works in isolation.) – fiedl Aug 05 '14 at 23:41
  • your answer does not solve the issue, but your `require 'pry'; binding.pry;` offers a new route to debug this problem that hasn't been suggested in related questions. If you would provide an answer with this trick, I'll award it the 150 reputation. (There are only 10 hours left to do so.) – fiedl Aug 06 '14 at 12:03