ibl*_*lue 10 rspec ruby-on-rails capybara
我的API允许用户购买某些独特的商品,其中每件商品只能出售给一个用户.因此,当多个用户尝试购买相同的项目时,一个用户应该得到响应:好的,另一个用户应该得到响应too_late.
现在,我的代码中似乎存在错误.竞争条件.如果两个用户试图在同一时间购买相同的项目,他们都得到了答案确定.这个问题在生产中显然是可重复的.现在我写了一个简单的测试,试图通过rspec重现它:
context "when I try to provoke a race condition" do
# ...
before do
@concurrent_requests = 2.times.map do
Thread.new do
Thread.current[:answer] = post "/api/v1/item/buy.json", :id => item.id
end
end
@answers = @concurrent_requests.map do |th|
th.join
th[:answer].body
end
end
it "should only sell the item to one user" do
@answers.sort.should == ["ok", "too_late"].sort
end
end
Run Code Online (Sandbox Code Playgroud)
好像不会同时执行查询.为了测试这个,我将以下代码放入我的控制器操作中:
puts "Is it concurrent?"
sleep 0.2
puts "Oh Noez."
Run Code Online (Sandbox Code Playgroud)
如果请求是并发的,则预期输出将是:
Is it concurrent?
Is it concurrent?
Oh Noez.
Oh Noez.
Run Code Online (Sandbox Code Playgroud)
但是,我得到以下输出:
Is it concurrent?
Oh Noez.
Is it concurrent?
Oh Noez.
Run Code Online (Sandbox Code Playgroud)
这告诉我,水豚的请求不会同时运行,而是一次运行一个.如何让我的capabara请求并发?
ibl*_*lue 14
多线程和水豚不起作用,因为Capabara使用一个单独的服务器线程,它顺序处理连接.但如果你分叉,它就可以了.
我使用退出代码作为进程间通信机制.如果你做更复杂的东西,你可能想使用套接字.
这是我的快速和肮脏的黑客:
before do
@concurrent_requests = 2.times.map do
fork do
# ActiveRecord explodes when you do not re-establish the sockets
ActiveRecord::Base.connection.reconnect!
answer = post "/api/v1/item/buy.json", :id => item.id
# Calling exit! instead of exit so we do not invoke any rspec's `at_exit`
# handlers, which cleans up, measures code coverage and make things explode.
case JSON.parse(answer.body)["status"]
when "accepted"
exit! 128
when "too_late"
exit! 129
end
end
end
# Wait for the two requests to finish and get the exit codes.
@exitcodes = @concurrent_requests.map do |pid|
Process.waitpid(pid)
$?.exitstatus
end
# Also reconnect in the main process, just in case things go wrong...
ActiveRecord::Base.connection.reconnect!
# And reload the item that has been modified by the seperate processs,
# for use in later `it` blocks.
item.reload
end
it "should only accept one of two concurrent requests" do
@exitcodes.sort.should == [128, 129]
end
Run Code Online (Sandbox Code Playgroud)
我使用相当奇特的退出代码,如128和129,因为如果未到达大小写块,进程将退出代码0,如果发生异常,则进程退出1.两者都不应该发生.因此,通过使用更高的代码,我会注意到出现问题.
您无法进行并发的水豚请求.但是,您可以创建多个capybara会话,并在同一测试中使用它们来模拟并发用户.
user_1 = Capybara::Session.new(:webkit) # or whatever driver
user_2 = Capybara::Session.new(:webkit)
user_1.visit 'some/page'
user_2.visit 'some/page'
# ... more tests ...
user_1.click_on 'Buy'
user_2.click_on 'Buy'
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
3736 次 |
| 最近记录: |