1

Similar to SimpleCov calculate 0% coverage for user model, but no joy.

I believe(/hope?) I am following the recommendations in Want to use Spring with SimpleCov?.

With the "load and start simplecov" code in spec/rails_helper.rb and running under Spring, my User class is getting loaded by devise_for in my routes.rb before simplecov is loaded.

Running without Spring — e.g., $ rspec — all is well.

What am I missing to get it to work under Spring?


  • ruby 2.2.1p85 (2015-02-26 revision 49769) [x86_64-darwin15]
  • simplecov 0.12.0
  • rails 4.2.5
  • spring 1.6.3
  • devise 3.5.6
  • factory_girl 4.5.0
  • factory_girl_rails 4.6.0

spec/rails_helper.rb:

ENV['RAILS_ENV'] ||= 'test'

if ENV['RAILS_ENV'] == 'test'
  puts 'loading simplecov'
  require 'simplecov'
  SimpleCov.start 'rails'
end

require File.expand_path('../../config/environment', __FILE__)
[...]

app/models/user.rb

class User
  puts 'loading User'
  puts caller
  [...]

config/routes.rb:

Rails.application.routes.draw do
  scope '(:locale)', locale: /en|es/ do
    root to: 'home#index'

    devise_for :users # this is line 6

Output:

$ spring stop; spring rspec
Spring is not running

loading User
<project-path>/app/models/user.rb:2:in `<top (required)>'
<gems-path>/activesupport-4.2.5/lib/active_support/dependencies.rb:457:in `load'
<gems-path>/activesupport-4.2.5/lib/active_support/dependencies.rb:457:in `block in load_file'
<gems-path>/activesupport-4.2.5/lib/active_support/dependencies.rb:647:in `new_constants_in'
<gems-path>/activesupport-4.2.5/lib/active_support/dependencies.rb:456:in `load_file'
<gems-path>/activesupport-4.2.5/lib/active_support/dependencies.rb:354:in `require_or_load'
<gems-path>/activesupport-4.2.5/lib/active_support/dependencies.rb:494:in `load_missing_constant'
<gems-path>/activesupport-4.2.5/lib/active_support/dependencies.rb:184:in `const_missing'
<gems-path>/activesupport-4.2.5/lib/active_support/inflector/methods.rb:261:in `const_get'
<gems-path>/activesupport-4.2.5/lib/active_support/inflector/methods.rb:261:in `block in constantize'
<gems-path>/activesupport-4.2.5/lib/active_support/inflector/methods.rb:259:in `each'
<gems-path>/activesupport-4.2.5/lib/active_support/inflector/methods.rb:259:in `inject'
<gems-path>/activesupport-4.2.5/lib/active_support/inflector/methods.rb:259:in `constantize'
<gems-path>/activesupport-4.2.5/lib/active_support/dependencies.rb:566:in `get'
<gems-path>/activesupport-4.2.5/lib/active_support/dependencies.rb:597:in `constantize'
<gems-path>/devise-3.5.6/lib/devise.rb:292:in `get'
<gems-path>/devise-3.5.6/lib/devise/mapping.rb:81:in `to'
<gems-path>/devise-3.5.6/lib/devise/mapping.rb:76:in `modules'
<gems-path>/devise-3.5.6/lib/devise/mapping.rb:93:in `routes'
<gems-path>/devise-3.5.6/lib/devise/mapping.rb:160:in `default_used_route'
<gems-path>/devise-3.5.6/lib/devise/mapping.rb:70:in `initialize'
<gems-path>/devise-3.5.6/lib/devise.rb:326:in `new'
<gems-path>/devise-3.5.6/lib/devise.rb:326:in `add_mapping'
<gems-path>/devise-3.5.6/lib/devise/rails/routes.rb:238:in `block in devise_for'
<gems-path>/devise-3.5.6/lib/devise/rails/routes.rb:237:in `each'
<gems-path>/devise-3.5.6/lib/devise/rails/routes.rb:237:in `devise_for'
<project-path>/config/routes.rb:6:in `block (2 levels) in <top (required)>'
<gems-path>/actionpack-4.2.5/lib/action_dispatch/routing/mapper.rb:817:in `scope'
<project-path>/config/routes.rb:3:in `block in <top (required)>'
<gems-path>/actionpack-4.2.5/lib/action_dispatch/routing/route_set.rb:434:in `instance_exec'
<gems-path>/actionpack-4.2.5/lib/action_dispatch/routing/route_set.rb:434:in `eval_block'
<gems-path>/actionpack-4.2.5/lib/action_dispatch/routing/route_set.rb:412:in `draw'
<project-path>/config/routes.rb:1:in `<top (required)>'
<gems-path>/activesupport-4.2.5/lib/active_support/dependencies.rb:268:in `load'
<gems-path>/activesupport-4.2.5/lib/active_support/dependencies.rb:268:in `block in load'
<gems-path>/activesupport-4.2.5/lib/active_support/dependencies.rb:240:in `load_dependency'
<gems-path>/activesupport-4.2.5/lib/active_support/dependencies.rb:268:in `load'
<gems-path>/railties-4.2.5/lib/rails/application/routes_reloader.rb:40:in `block in load_paths'
<gems-path>/railties-4.2.5/lib/rails/application/routes_reloader.rb:40:in `each'
<gems-path>/railties-4.2.5/lib/rails/application/routes_reloader.rb:40:in `load_paths'
<gems-path>/railties-4.2.5/lib/rails/application/routes_reloader.rb:16:in `reload!'
<gems-path>/railties-4.2.5/lib/rails/application/routes_reloader.rb:26:in `block in updater'
<gems-path>/activesupport-4.2.5/lib/active_support/file_update_checker.rb:75:in `call'
<gems-path>/activesupport-4.2.5/lib/active_support/file_update_checker.rb:75:in `execute'
<gems-path>/railties-4.2.5/lib/rails/application/routes_reloader.rb:27:in `updater'
<gems-path>/railties-4.2.5/lib/rails/application/routes_reloader.rb:7:in `execute_if_updated'
<gems-path>/railties-4.2.5/lib/rails/application/finisher.rb:69:in `block in <module:Finisher>'
<gems-path>/railties-4.2.5/lib/rails/initializable.rb:30:in `instance_exec'
<gems-path>/railties-4.2.5/lib/rails/initializable.rb:30:in `run'
<gems-path>/railties-4.2.5/lib/rails/initializable.rb:55:in `block in run_initializers'
<ruby-path>/lib/ruby/2.2.0/tsort.rb:226:in `block in tsort_each'
<ruby-path>/lib/ruby/2.2.0/tsort.rb:348:in `block (2 levels) in each_strongly_connected_component'
<ruby-path>/lib/ruby/2.2.0/tsort.rb:429:in `each_strongly_connected_component_from'
<ruby-path>/lib/ruby/2.2.0/tsort.rb:347:in `block in each_strongly_connected_component'
<ruby-path>/lib/ruby/2.2.0/tsort.rb:345:in `each'
<ruby-path>/lib/ruby/2.2.0/tsort.rb:345:in `call'
<ruby-path>/lib/ruby/2.2.0/tsort.rb:345:in `each_strongly_connected_component'
<ruby-path>/lib/ruby/2.2.0/tsort.rb:224:in `tsort_each'
<ruby-path>/lib/ruby/2.2.0/tsort.rb:203:in `tsort_each'
<gems-path>/railties-4.2.5/lib/rails/initializable.rb:54:in `run_initializers'
<gems-path>/railties-4.2.5/lib/rails/application.rb:352:in `initialize!'
<project-path>/config/environment.rb:5:in `<top (required)>'
<gems-path>/activesupport-4.2.5/lib/active_support/dependencies.rb:274:in `require'
<gems-path>/activesupport-4.2.5/lib/active_support/dependencies.rb:274:in `block in require'
<gems-path>/activesupport-4.2.5/lib/active_support/dependencies.rb:240:in `load_dependency'
<gems-path>/activesupport-4.2.5/lib/active_support/dependencies.rb:274:in `require'
<gems-path>/spring-1.6.3/lib/spring/application.rb:92:in `preload'
<gems-path>/spring-1.6.3/lib/spring/application.rb:143:in `serve'
<gems-path>/spring-1.6.3/lib/spring/application.rb:131:in `block in run'
<gems-path>/spring-1.6.3/lib/spring/application.rb:125:in `loop'
<gems-path>/spring-1.6.3/lib/spring/application.rb:125:in `run'
<gems-path>/spring-1.6.3/lib/spring/application/boot.rb:18:in `<top (required)>'
<ruby-path>/lib/ruby/site_ruby/2.2.0/rubygems/core_ext/kernel_require.rb:54:in `require'
<ruby-path>/lib/ruby/site_ruby/2.2.0/rubygems/core_ext/kernel_require.rb:54:in `require'
-e:1:in `<main>'
Running via Spring preloader in process 25001
loading simplecov
[...]
Coverage report generated for RSpec to <project-path>/coverage. 212 / 368 LOC (57.61%) covered.

TIA!

Community
  • 1
  • 1
Bob Mazanec
  • 1,121
  • 10
  • 18
  • 1
    If I `puts caller` in routes.rb I see a very similar stack trace. Loading routes early seems to be standard Rails behavior. Perhaps you can skip the devise call if `Rails.env.test?` and make it later in rails_helper.rb. Not in a position to try that myself. – Dave Schweisguth Aug 17 '16 at 13:41

1 Answers1

1

The Rails project that I have handy has this problem. (I didn't notice until now because I always run specs with rake as part of an RSpec + Cucumber suite, and the rspec rake task forks, which escapes the class loading done by spring. Using rake spec wouldn't be a good solution to your problem since forking negates spring's speed improvement.) That project does not use devise. In that project, this problem was caused by two things:

  1. Eager loading classes in the test environment

    I had config.eager_load = true in my config/environments/test.rb (so that unused classes would appear in coverage results). I changed it to false. spring rspec coverage improved but was still not what it should be.

    Note that commenting out config.eager_load = true leaves eager_load nil, which is treated as true. One has to explicitly say false.

    Note also that simplecov also recommends setting config.serve_static_files = false. That didn't make any difference in my project.

  2. factory_girl

    The backtrace when a model was loaded then included these lines:

    <gems path>/factory_girl-4.7.0/lib/factory_girl/find_definitions.rb:15:in `find_definitions'
    <gems path>/factory_girl_rails-4.7.0/lib/factory_girl_rails/railtie.rb:21:in `block in <class:Railtie>'
    

    I commented the following out of factory_girl_rails' lib/factory_girl_rails/railtie.rb

    # config.after_initialize do
    #   FactoryGirl.find_definitions
    # 
    #   if defined?(Spring)
    #     Spring.after_fork { FactoryGirl.reload }
    #   end
    # end
    

    and added FactoryGirl.find_definitions to my rails_helper.rb immediately after require 'simplecov'. spring rspec then collected coverage correctly.

    This part of the problem seems to be a consequence of factory_girl's design. I'm not aware of a solution nicer than modifying factory_girl.

Note that similar problems are caused by other kinds of early class loading. Sometimes the solution is to start simplecov in config/spring.rb. That didn't work for my project. If you don't eager load in tests or use factory_girl this might be your solution.

Dave Schweisguth
  • 36,475
  • 10
  • 98
  • 121
  • Already had the rails config options false. Putting the simplecov load & start in config/spring.rb makes no difference. I do, in fact use, factory_girl. After making the recommended changes, the output shows User loading before simplecov...but still reports 0% coverage :-( Still, thanks for the leads & ideas! – Bob Mazanec Aug 17 '16 at 02:09
  • 1
    Better put the entire backtrace in the question. Fixing the problem on my project was all about understanding the various places where eager loading occurred. – Dave Schweisguth Aug 17 '16 at 02:57