2012-06-26 36 views
9

API của tôi cho phép người dùng mua một số mặt hàng duy nhất, trong đó mỗi mục chỉ có thể được bán cho một người dùng. Vì vậy, khi nhiều người dùng cố gắng mua cùng một mặt hàng, một người dùng sẽ nhận được phản hồi: ok và người dùng khác sẽ nhận được phản hồi too_late.Yêu cầu Capybara đồng thời nhiều luồng?

Hiện tại, có vẻ như có lỗi trong mã của tôi. Một điều kiện chủng tộc. Nếu hai người dùng cố gắng mua cùng một mục cùng một lúc, cả hai đều nhận được câu trả lời ok. Vấn đề này có thể tái sản xuất rõ ràng. Bây giờ tôi đã viết một bài kiểm tra đơn giản cố gắng tái tạo nó qua 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 

Có vẻ như không thực hiện các truy vấn cùng một lúc. Để kiểm tra điều này, tôi đặt đoạn mã sau vào hành động điều khiển của tôi:

puts "Is it concurrent?" 
sleep 0.2 
puts "Oh Noez." 

Dự kiến ​​sản lượng sẽ là, nếu yêu cầu được đồng thời:

Is it concurrent? 
Is it concurrent? 
Oh Noez. 
Oh Noez. 

Tuy nhiên, tôi nhận được kết quả như sau:

Is it concurrent? 
Oh Noez. 
Is it concurrent? 
Oh Noez. 

Điều này cho tôi biết rằng các yêu cầu capybara không được chạy đồng thời, nhưng mỗi lần một yêu cầu. Làm cách nào để yêu cầu capabara của tôi đồng thời?

+0

Ví dụ mã ở trên của bạn không giống như DSL hiện tại của Capybara đối với tôi. Nó trông giống như một thử nghiệm điều khiển đơn giản bằng cách sử dụng Rack :: Test. Đây co thật sự la bản chât của no? –

Trả lời

13

Đa luồng và capybara không hoạt động, vì Capabara sử dụng chuỗi máy chủ riêng biệt xử lý kết nối tuần tự. Nhưng nếu bạn ngã ba, nó hoạt động.

Tôi đang sử dụng mã thoát như một cơ chế giao tiếp giữa các quá trình. Nếu bạn làm những thứ phức tạp hơn, bạn có thể muốn sử dụng ổ cắm.

Đây là cách nhanh chóng và dơ bẩn hack của tôi:

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 

tôi sử dụng mã lối ra khá kỳ lạ như và , bởi vì quá trình thoát với mã 0 nếu khối trường hợp không đạt được và 1 nếu một ngoại lệ xảy ra. Cả hai không nên xảy ra. Vì vậy, bằng cách sử dụng mã cao hơn, tôi nhận thấy khi mọi thứ xảy ra sai.

+0

Giải pháp tốt! Cũng giống như một tham chiếu, bạn có thể đăng các mã điều khiển/mô hình có liên quan mà biểu hiện điều kiện chủng tộc không? –

+0

Không thể tin rằng câu hỏi và câu trả lời này chưa được bỏ phiếu. Lưu ngày của tôi! –

+0

Giải pháp tuyệt vời! Mni thx để chia sẻ! – ctp

5

Bạn không thể thực hiện yêu cầu capybara đồng thời. Tuy nhiên, bạn có thể tạo nhiều phiên capybara và sử dụng chúng trong cùng một thử nghiệm để mô phỏng người dùng đồng thời.

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' 
+1

Tôi biết về các yêu cầu tuần tự. Cuối cùng tôi đã tự giải quyết được vấn đề. Xem câu trả lời của tôi. Tôi ** có thể ** thực hiện các yêu cầu đồng thời. – iblue

Các vấn đề liên quan