simplecov: SimpleCov showing incorrect coverage result when parallelize is enabled in Rails 6.0.0 beta3
Rails version: 6.0.0 beta3 Ruby version: 2.6.1
Issue We have configured our test helper file as below
require "simplecov"
SimpleCov.start do
add_filter "/test/"
add_group "Models", "app/models"
end
ENV["RAILS_ENV"] ||= "test"
require_relative "../config/environment"
require "rails/test_help"
class ActiveSupport::TestCase
parallelize(workers: :number_of_processors)
fixtures :all
end
On executing rake test the coverage report is incorrect even when test cases are written of the method. So this is our user model
class User < ApplicationRecord
devise :database_authenticatable, :registerable, :trackable,
:recoverable, :rememberable, :validatable
has_one_attached :avatar
validates :email, uniqueness: true
def admin?
self.role == "admin"
end
def name
"#{first_name} #{last_name}"
end
end
and the test cases we have are
def test_user_admin
user = users :admin
assert user.admin?
end
def test_user_is_not_an_admin
user = users :albert
assert_not user.admin?
end
def test_should_return_first_name_and_last_name_as_name
user = users :albert
assert_equal "Albert Smith", user.name
end
But the coverage report when parallelize is enabled is as below

and when it is commented out the test results are all green

I tried below approaches but all of them give correct report only when parallelize is commented:
- https://github.com/colszowka/simplecov/issues/235#issuecomment-22271831
- Created a
.simplecovfile in root directory and executedrake. - https://github.com/colszowka/simplecov/issues?utf8=✓&q=is%3Aissue+is%3Aopen+parallel followed the issues and tried other approaches.
But I am still not able to get the correct coverage result when parallelize is enabled.
Many thanks in advance.
About this issue
- Original URL
- State: open
- Created 5 years ago
- Reactions: 40
- Comments: 16
Commits related to this issue
- Disable parallel tests to fix test coverage SimpleCov does not support running tests in parallel yet, a feature introduced only recently by Rails 6 [1]. Since our test suite is still quite small, and... — committed to jdno/venja by jdno 5 years ago
- Disable parallel tests to fix test coverage SimpleCov does not support running tests in parallel yet, a feature introduced only recently by Rails 6 [1]. Since our test suite is still quite small, and... — committed to jdno/venja by jdno 5 years ago
- Disable parallel tests to fix test coverage SimpleCov does not support running tests in parallel yet, a feature introduced only recently by Rails 6 [1]. Since our test suite is still quite small, and... — committed to jdno/venja by jdno 5 years ago
- disable parallelize test. see. https://github.com/colszowka/simplecov/issues/718 — committed to miyohide/my_rails_template by miyohide 4 years ago
- tests: fix test coverage reporting for parallel testing. * Fix simplecov clobbering test coverage reports when using parallel tests. * Generate coverage reports by default (remove $SIMPLECOV flag). *... — committed to danbooru/danbooru by evazion 4 years ago
- Make coverage testing work with parallel tests https://github.com/simplecov-ruby/simplecov/issues/718#issuecomment-538201587 — committed to tomhughes/openstreetmap-website by tomhughes 4 years ago
- Fix SimpleCov following issue https://github.com/simplecov-ruby/simplecov/issues/718 Rails6のパラレルテスト対策 — committed to universato/BrainDriller by universato 3 years ago
- Fix coverage for parallel tests https://github.com/simplecov-ruby/simplecov/issues/718#issuecomment-538201587 — committed to thomasregnet/kleinodien by thomasregnet a year ago
The solution by @bbascarevic-tti is almost there. Here’s what I ended up with; it seems to be working as expected:
There are a few changes from @bbascarevic-tti’s solution:
SimpleCov.pidtoProcess.pidin the child process. This causes SimpleCov to report a failure for every single child process because SimpleCov then thinks that the child process is the coordinator.SimpleCov.use_mergingsince it defaults totrue.SimpleCov.at_exithook - these are not run in the workers anyway.parallelize_teardownhook to callSimpleCov.result. This complex method generates the result and, ifSimpleCov.use_mergingis true (which it is by default), stores the result for later merging. This allows the results of all workers to be merged into a single result.Here’s an example output:
I’ve run the wipe / re-run without Spring many times and receive a consistent result so I don’t believe there are any race conditions in the process model. When you mix in Spring, you get the lovely problems inherent in such a venture, so YMMV if you choose to try it with Spring.
Threads
Note that I’ve only been able to produce completely consistent results using process parallelization. When I tested out
with: :threads, I noticed that the coverage numbers would non-deterministically jump between58 / 148and49 / 148. There were also errors due to thread deadlocks in the database too though, so I think threads on MRI just aren’t fully baked (which is fine since you don’t get true parallelism anyway).Next Steps
I haven’t been able to figure out a way to upstream this workflow into SimpleCov, so if someone has any bright ideas, please feel free to do that! The way
parallel_testssets the environment variables and gives you access to both the current worker ID and the total number of workers makes sense. Perhaps Rails could use similar functionality to pass the total number of workers into theparallelize_setupandparallelize_teardownhooks?And as a small update, I did get the ruby together fund and hence an improved rails support especially its new test parallelization is towards the top of the list (after branch coverage and some friends though)
Yes this is a biggie. Sorry for taking so long to respond, I took a bit of a break from maintaining simplecov.
Making simplecov completely work with rails and its new parallelization will be a bigger undertaking I fear. PRs are welcome. I’ll see if I can allocate the time for it but I’m unsure.
Thank you for reporting 💚
Any interest in maybe documenting @michaelherold’s (excellent! working!) approach in the README, until/unless built-in support for Rails parallelization is added?
Now that Rails will only start parallelizing tests once you hit 50 of them, the fact coverage will suddenly go from correct to near-0% is really confusing and takes time to piece together by searching issues in the repo. (The optimization is totally valid, but when a working initial setup stops working suddenly it can be disorienting.)
As a workaround we can piggy-back on the already existing feature of result merging, we just need to trick SimpleCov that each parallel run is another Command. We set
SimpleCov.use_mergingtotruein the root process, and then in each fork we change the command name so forks would not overwrite each others’ results.@alkesh26 I don’t have a solution for you but have link to my app and reproducible steps:
parallelize(workers: :number_of_processors)rm -rf coverage/; RAILS_ENV=test ./bin/rails test; open coverage/index.htmlapp/lib/watermelon/example.rbshows 0%parallelize(workers: :number_of_processors)rm -rf coverage/; RAILS_ENV=test ./bin/rails test; open coverage/index.htmlapp/lib/watermelon/example.rbshows 70%Omit loading
springfrombin/railsandbin/rakefrom Rails 6 application. Loading spring server has his side effectsI think the problem was either that I still had
parallelize(workers: 1)in my test setup OR that I actually didn’t have any system tests yet. As soon as I removedparallelizecompletely and added a working spec, the test coverage is working fine forrails test:system test🎉This may be common knowledge, but make sure you’re also eager_loading your testing environment. I followed the above advice, but still had some accuracy issues until I discovered this. Just run with
CI=true bin/rails test:all