Parallel testing causes DRb error in Rails 6.0.0.rc1
Closed this issue · 6 comments
I've been using your gem on a project (Rails 6.0.0.rc1) but had to set parallelize(workers: 1)
because I keep on getting the following error.
/.rbenv/versions/2.5.1/lib/ruby/2.5.0/drb/drb.rb:1733:in 'current_server': DRb::DRbServerNotFound (DRb::DRbConnError)
I've created an app that errors here - https://github.com/benaldred/rails-6-minitest-drb-error
- It's a vanilla rails 6 rc1 app with the install instructions followed from this projects README.
- Ran tests and then ran fine with no DB
- Ran the generator to add a
User
model and un commented the tests - Ran tests
rails test
and I got the error
I had to remove spring. It kept on hanging and I think I read that parallel tests don't work with Spring.
If I set parallelize(workers: 1)
all works as expected
Any ideas?
Very interesting. If you change the describe User do
in your user_test.rb
file to class UserTest < ActiveSupport::TestCase
then it works just fine. So something is definitely up with how the spec DSL is interacting with parallelize
.
Thanks for the report!
I think this has to do with how the spec DSL creates anonymous classes for tests. When defining the test using a class the job that is being pushed onto the Drb queue looks like this:
[UserTest, "test_0001_does a thing", #<Minitest::CompositeReporter:0x...>]
And when it is popped off the queue it looks like this:
[UserTest, "test_0001_does a thing", #<DRb::DRbObject:0x...>]
Notice how UserTest
is the same because it is a class and ruby knows about the class. But, when defining the test using describe
(the spec DSL), the job that is pushed onto the Drb queue includes the test class as defined by the spec DSL, which is not named:
[#<Class:0x00007f8fc6d0aab8>, "test_0001_does a thing", #<Minitest::CompositeReporter:0x...>]
And when this spec test array is popped off the queue it looks like this:
#<DRb::DRbObject:0x00007fd32fc5e2c0 @uri="drbunix:/var/folders/n4/hn9lkdc93xq18x8b50wggy_r0000gn/T/druby54823.0", @ref=70272544215120>
But we can still treat it like an array, this is not where the error is coming from. The first element is the class, but it is now a DRbObject
. The second element is a string of the test name, and the third is the reporter as a DRbObject
object.
The issue seems to be passing the test class as defined by the spec DSL through Drb.
@benaldred Can you try adding the following to your test/test_helper.rb
file?
module Minitest
module Rails
module SpecTests
end
end
end
module Kernel
alias_method :describe_before_minitest_spec_constant_fix, :describe
private :describe_before_minitest_spec_constant_fix
def describe *args, &block
cls = describe_before_minitest_spec_constant_fix *args, &block
::Minitest::Rails::SpecTests.const_set "Test__#{cls.object_id}", cls
cls
end
end
Here is another option:
module Minitest
module Rails
module SpecTests
end
end
end
module Kernel
alias_method :describe_before_minitest_spec_constant_fix, :describe
private :describe_before_minitest_spec_constant_fix
def describe *args, &block
cls = describe_before_minitest_spec_constant_fix *args, &block
cls_const = "Test__#{cls.name.to_s.split(/\W/).reject(&:empty?).join("_".freeze)}"
if block.source_location
source_path, line_num = block.source_location
source_path = Pathname.new(source_path).relative_path_from(Rails.root).to_s
source_path = source_path.split(/\W/).reject(&:empty?).join("_".freeze)
cls_const += "__#{source_path}__#{line_num}"
end
while Minitest::Rails::SpecTests.const_defined? cls_const
cls_const += "_1"
end
Minitest::Rails::SpecTests.const_set cls_const, cls
cls
end
end
When changing the test/models/user_test.rb
file to this:
require "test_helper"
describe User do
it "does a thing" do
value(1+1).must_equal 2
end
end
describe User do
it "does another thing" do
value(1+1).must_equal 2
end
end
describe User do
it "does a third thing" do
value(1+1).must_equal 2
end
end
[1, 2, 3].each do |num|
describe User, :model do
it "works when recursed" do
value(num).wont_be_nil
end
describe :nested do
it "still works" do
assert true
end
end
end
end
It created the following constants:
Minitest::Rails::SpecTests::Test__User__test_models_user_test_rb__3
Minitest::Rails::SpecTests::Test__User__test_models_user_test_rb__9
Minitest::Rails::SpecTests::Test__User__test_models_user_test_rb__15
Minitest::Rails::SpecTests::Test__User_model_nested__test_models_user_test_rb__27
Minitest::Rails::SpecTests::Test__User_model__test_models_user_test_rb__22
Minitest::Rails::SpecTests::Test__User_model_nested__test_models_user_test_rb__27_1
Minitest::Rails::SpecTests::Test__User_model__test_models_user_test_rb__22_1
Minitest::Rails::SpecTests::Test__User_model_nested__test_models_user_test_rb__27_1_1
Minitest::Rails::SpecTests::Test__User_model__test_models_user_test_rb__22_1_1
I can confirm both bits of code work. I've also tried it on another project with a lot more tests and it seems fine there too.
Thanks
Thank you for checking. I'll merge the PR then.