问题
I have below classes(only for example),
class Background
def self.add_thread(&blcok)
Thread.new do
yield
ActiveRecord::Base.connection.close
end
end
end
class Email
def send_email_in_other_thread
Background.add_thread do
send_email
end
end
def send_email
UserMailer.greeting_email.deliver_now
end
end
And below codes are for tests,
class EmailTest < ActiveSupport::TestCase
class Background
def self.add_thread(&block)
yield
end
end
test 'should send email' do
assert_difference 'ActionMailer::Base.deliveries.size', 1 do
send_email_in_other_thread
end
end
end
But this test fails, "ActionMailer::Base.deliveries.size" didn't change by 1.
And 1 in about 20 times success.
I think it is because of the modified Background class. Maybe overriding in test doesn't work or yield proc is not executed instantly but in delayed.
I tried 'block.call' instead of yield, but the result is same.
How can I make this test always be success?
回答1:
This looks like a classic race condition. Thread.new
returns as soon as the thread is spawned, not when it's work is completed.
Because your main thread doesn't halt execution, most of the time your assertion is run before the mail has been sent.
You could use the join method to wait for the sending thread to finish execution before returning, but then it would essentially be equivalent to a single thread again, as it blocks the calling (main) thread until work is done.
Thread.new do
yield
ActiveRecord::Base.connection.close
end.join
There are already some great gems, however, for dealing with background jobs in Rails. Check out SideKiq and Resque for example.
来源:https://stackoverflow.com/questions/32677966/how-can-i-overriding-class-with-proc-and-yield-in-test-on-rails