capybara-webkit 不稳定的 Sidekiq 工作行为
Unstable Sidekiq job behavior with capybara-webkit
我正在维护一套功能规范,其中许多 sidekiq 作业在测试中 运行。一切都很好,直到需要为所有功能规范启用 javascript,事情突然变得很糟糕。我不确定我是否理解其中的原因。
典型的问题如下所示:
在规范的开头我调用了 helper,它最终启动了 Sidekiq 作业:
it 'does something fancy' do
Sidekiq::Testing.inline! do
page.attach_file('black_list_file', file)
click_on 'Import file'
end
expect(page).to have_content('Import started') # No problem here
expect(page).to have_link('imported_file.csv') # This fails
end
我尝试将 sleep n
放在 expect
语句之间 - 即使 n = 10
也不走运。然后我尝试调试,并注意到一些奇怪的事情:ImportWorker.jobs
returns 作业仍在队列中,如果我从调试器明确声明 ImportWorker.drain
,规范将通过。
但是!如果我将 ImportWorker.drain
放在 expect
行之间 - 即使我将 sleep
放在 drain 之前,它仍然会失败,等待请求实际调用 worker 上的 perform_async
。
当我切换到 selenium 驱动程序时,事情变得更加奇怪 - 一切顺利,测试通过。
现在,我对如何在规范中的不同线程中处理来自水豚的请求有了一些了解,如 here 所述。所以很可能,当调用 worker 时,它不知道 inline!
,只是将作业放入 Redis 队列。我仍然不明白为什么 Selenium 能够解决这个问题。如果这是真的,我如何使用 webkit 驱动程序测试涉及 sidekiq 作业的功能。
UPD.
好的,如我所料,Sidekiq::Testing.inline?
returns false 在调用 worker 的地方,即使请求是从 inline!
块发出的。
这解决了所有问题:
if Rails.env.test?
Sidekiq::Testing.inline! do
ImportWorker.perform_async(args)
end
else
ImportWorker.perform_async(args)
end
显然,这与最佳实践相去甚远,所以我正在考虑编写一个 class,它将接受工作人员名称作为字符串或符号,并使用 or 调用执行没有 inline!
块取决于环境。这样我就可以把这种丑陋的东西放在一个地方,而不需要污染控制器和模型。
如果您认为这是一个糟糕的解决方案,请发表评论。
全部Sidekiq::Testing.内联! does(当传递一个块时)在执行块之前设置一个全局(非线程特定)设置,然后在块完成时重置该设置。我相信您的测试失败的原因是,即使您在块内单击 button/link,在设置全局内联设置时,作业实际上并没有被添加到队列中。这是因为 #click_on
returns 在单击后立即执行,不需要等待该单击的任何副作用发生(表单提交、JS 行为等)。如果您将您的期望(等待副作用发生)移动到块内,那么测试应该通过。
it 'does something fancy' do
Sidekiq::Testing.inline! do
page.attach_file('black_list_file', file)
click_on 'Import file'
expect(page).to have_content('Import started') # No problem here
expect(page).to have_link('imported_file.csv')
end
end
另一种选择是使用 RSpec around 块根据测试中设置的元数据更改 Sidekiq 测试设置 - 添加类似以下内容的 RSpec 配置(注意:未尝试)
RSpec.configure do |config|
config.around(:example, :sidekiq_inline) do |example|
Sidekiq::Testing.inline! do
example.run
end
end
end
然后应该让你做
it 'does something fancy', :sidekiq_inline do
page.attach_file('black_list_file', file)
click_on 'Import file'
expect(page).to have_content('Import started') # No problem here
expect(page).to have_link('imported_file.csv')
end
您也可以将诸如清除 sidekiq 队列之类的内容添加到 around 块中。
我正在维护一套功能规范,其中许多 sidekiq 作业在测试中 运行。一切都很好,直到需要为所有功能规范启用 javascript,事情突然变得很糟糕。我不确定我是否理解其中的原因。
典型的问题如下所示: 在规范的开头我调用了 helper,它最终启动了 Sidekiq 作业:
it 'does something fancy' do
Sidekiq::Testing.inline! do
page.attach_file('black_list_file', file)
click_on 'Import file'
end
expect(page).to have_content('Import started') # No problem here
expect(page).to have_link('imported_file.csv') # This fails
end
我尝试将 sleep n
放在 expect
语句之间 - 即使 n = 10
也不走运。然后我尝试调试,并注意到一些奇怪的事情:ImportWorker.jobs
returns 作业仍在队列中,如果我从调试器明确声明 ImportWorker.drain
,规范将通过。
但是!如果我将 ImportWorker.drain
放在 expect
行之间 - 即使我将 sleep
放在 drain 之前,它仍然会失败,等待请求实际调用 worker 上的 perform_async
。
当我切换到 selenium 驱动程序时,事情变得更加奇怪 - 一切顺利,测试通过。
现在,我对如何在规范中的不同线程中处理来自水豚的请求有了一些了解,如 here 所述。所以很可能,当调用 worker 时,它不知道 inline!
,只是将作业放入 Redis 队列。我仍然不明白为什么 Selenium 能够解决这个问题。如果这是真的,我如何使用 webkit 驱动程序测试涉及 sidekiq 作业的功能。
UPD.
好的,如我所料,Sidekiq::Testing.inline?
returns false 在调用 worker 的地方,即使请求是从 inline!
块发出的。
这解决了所有问题:
if Rails.env.test?
Sidekiq::Testing.inline! do
ImportWorker.perform_async(args)
end
else
ImportWorker.perform_async(args)
end
显然,这与最佳实践相去甚远,所以我正在考虑编写一个 class,它将接受工作人员名称作为字符串或符号,并使用 or 调用执行没有 inline!
块取决于环境。这样我就可以把这种丑陋的东西放在一个地方,而不需要污染控制器和模型。
如果您认为这是一个糟糕的解决方案,请发表评论。
全部Sidekiq::Testing.内联! does(当传递一个块时)在执行块之前设置一个全局(非线程特定)设置,然后在块完成时重置该设置。我相信您的测试失败的原因是,即使您在块内单击 button/link,在设置全局内联设置时,作业实际上并没有被添加到队列中。这是因为 #click_on
returns 在单击后立即执行,不需要等待该单击的任何副作用发生(表单提交、JS 行为等)。如果您将您的期望(等待副作用发生)移动到块内,那么测试应该通过。
it 'does something fancy' do
Sidekiq::Testing.inline! do
page.attach_file('black_list_file', file)
click_on 'Import file'
expect(page).to have_content('Import started') # No problem here
expect(page).to have_link('imported_file.csv')
end
end
另一种选择是使用 RSpec around 块根据测试中设置的元数据更改 Sidekiq 测试设置 - 添加类似以下内容的 RSpec 配置(注意:未尝试)
RSpec.configure do |config|
config.around(:example, :sidekiq_inline) do |example|
Sidekiq::Testing.inline! do
example.run
end
end
end
然后应该让你做
it 'does something fancy', :sidekiq_inline do
page.attach_file('black_list_file', file)
click_on 'Import file'
expect(page).to have_content('Import started') # No problem here
expect(page).to have_link('imported_file.csv')
end
您也可以将诸如清除 sidekiq 队列之类的内容添加到 around 块中。