在 RSpec 单元测试中模拟竞争条件 [英] Simulating race conditions in RSpec unit tests

查看:72
本文介绍了在 RSpec 单元测试中模拟竞争条件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我们有一个异步任务,它为一个对象执行一个潜在的长时间运行的计算.然后将结果缓存在对象上.为了防止多个任务重复相同的工作,我们通过原子 SQL 更新添加了锁定:

We have an asynchronous task that performs a potentially long-running calculation for an object. The result is then cached on the object. To prevent multiple tasks from repeating the same work, we added locking with an atomic SQL update:

UPDATE objects SET locked = 1 WHERE id = 1234 AND locked = 0

锁定只针对异步任务.对象本身仍然可以由用户更新.如果发生这种情况,旧版本对象的任何未完成任务都应丢弃其结果,因为它们可能已过时.使用原子 SQL 更新也很容易做到这一点:

The locking is only for the asynchronous task. The object itself may still be updated by the user. If that happens, any unfinished task for an old version of the object should discard its results as they're likely out-of-date. This is also pretty easy to do with an atomic SQL update:

UPDATE objects SET results = '...' WHERE id = 1234 AND version = 1

如果对象已更新,则其版本将不匹配,因此结果将被丢弃.

If the object has been updated, its version won't match and so the results will be discarded.

这两个原子更新应该处理任何可能的竞争条件.问题是如何在单元测试中验证这一点.

These two atomic updates should handle any possible race conditions. The question is how to verify that in unit tests.

第一个信号量很容易测试,因为它只是针对两种可能的场景设置两个不同的测试:(1) 对象被锁定和 (2) 对象未被锁定.(我们不需要测试 SQL 查询的原子性,因为这应该是数据库供应商的责任.)

The first semaphore is easy to test, as it is simply a matter of setting up two different tests with the two possible scenarios: (1) where the object is locked and (2) where the object is not locked. (We don't need to test the atomicity of the SQL query as that should be the responsibility of the database vendor.)

如何测试第二个信号量?该对象需要在第一个信号量之后但在第二个之前的某个时间由第三方更改.这将需要暂停执行,以便可以可靠且一致地执行更新,但我知道不支持使用 RSpec 注入断点.有没有办法做到这一点?或者我是否忽略了其他一些模拟此类竞争条件的技术?

How does one test the second semaphore? The object needs to be changed by a third party some time after the first semaphore but before the second. This would require a pause in execution so that the update may be reliably and consistently performed, but I know of no support for injecting breakpoints with RSpec. Is there a way to do this? Or is there some other technique I'm overlooking for simulating such race conditions?

推荐答案

您可以借鉴电子制造的想法,将测试挂钩直接放入生产代码中.就像电路板可以制造出专门的地方供测试设备控制和探测电路一样,我们也可以用代码做同样的事情.

You can borrow an idea from electronics manufacturing and put test hooks directly into the production code. Just as a circuit board can be manufactured with special places for test equipment to control and probe the circuit, we can do the same thing with the code.

假设我们有一些代码向数据库中插入一行:

SUppose we have some code inserting a row into the database:

class TestSubject

  def insert_unless_exists
    if !row_exists?
      insert_row
    end
  end

end

但是此代码在多台计算机上运行.因此,存在竞争条件,因为另一个进程可能会在我们的测试和插入之间插入行,从而导致 DuplicateKey 异常.我们想测试我们的代码是否处理了由该竞争条件导致的异常.为此,我们的测试需要在调用 row_exists? 之后但在调用 insert_row 之前插入行.所以让我们在那里添加一个测试钩子:

But this code is running on multiple computers. There's a race condition, then, since another processes may insert the row between our test and our insert, causing a DuplicateKey exception. We want to test that our code handles the exception that results from that race condition. In order to do that, our test needs to insert the row after the call to row_exists? but before the call to insert_row. So let's add a test hook right there:

class TestSubject

  def insert_unless_exists
    if !row_exists?
      before_insert_row_hook
      insert_row
    end
  end

  def before_insert_row_hook
  end

end

在野外运行时,钩子除了占用一点 CPU 时间之外什么都不做.但是当代码被测试竞争条件时,测试猴子补丁 before_insert_row_hook:

When run in the wild, the hook does nothing except eat up a tiny bit of CPU time. But when the code is being tested for the race condition, the test monkey-patches before_insert_row_hook:

class TestSubject
  def before_insert_row_hook
    insert_row
  end
end

这不是很狡猾吗?就像寄生黄蜂幼虫劫持了毫无戒心的毛毛虫的身体一样,测试劫持了被测代码,以便创建我们需要测试的确切条件.

Isn't that sly? Like a parasitic wasp larva that has hijacked the body of an unsuspecting caterpillar, the test hijacked the code under test so that it will create the exact condition we need tested.

这个想法和XOR游标一样简单,所以我怀疑很多程序员已经独立发明了它.我发现它对于测试具有竞争条件的代码通常很有用.我希望它有所帮助.

This idea is as simple as the XOR cursor, so I suspect many programmers have independently invented it. I have found it to be generally useful for testing code with race conditions. I hope it helps.

这篇关于在 RSpec 单元测试中模拟竞争条件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆