我们有执行用于物体的潜在的长期运行的计算异步任务。然后将结果缓存的对象。为了防止多个任务从重复同样的工作,我们添加用原子SQL更新锁定:

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

在锁定仅用于异步任务。对象本身仍可以由用户进行更新。如果出现这种情况,因为他们很可能外的日期为旧版本的对象的任何未完成的任务应该抛弃它的结果。这也是很容易用原子SQL更新的事:

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

如果该对象已被更新,其版本将不匹配,所以结果将被丢弃。

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

在第一信号是容易测试的,因为它是简单地与两种可能的情况设置两种不同的测试的问题:(1)其中对象被锁定和(2),其中所述对象未锁定。 (我们并不需要测试SQL查询的原子作为应该是数据库供应商的责任。)

一个人如何测试第二旗语?需要由第三方一段时间的第一旗语之后,但在第二个改变的对象。这就需要在执行暂停,以便更新可能是可靠和一致的方式进行,但我知道使用RSpec注入断点不支持的。有没有办法做到这一点?还是有我俯瞰模拟这样的比赛条件,一些其他的技术?

有帮助吗?

解决方案

可以借用电子制造的想法,并把测试钩直接进入生产代码。只是作为电路板可以用特殊的地方来制造测试设备来控制和探测电路,我们可以做同样的事情的代码。

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

class TestSubject

  def insert_unless_exists
    if !row_exists?
      insert_row
    end
  end

end

但是,该代码在多台计算机上运行。有一个竞争条件,那么,因为另一个进程可以插入我们的测试,我们的插入的行,造成DuplicateKey例外。我们要测试我们的代码处理来自该比赛条件造成的除外。为了做到这一点,我们的测试需要调用row_exists?之后,但在调用insert_row之前插入的行。因此,让我们添加一个测试钩在那里:

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:

class TestSubject
  def before_insert_row_hook
    insert_row
  end
end

是不是很狡猾?像已经被劫持不知情毛虫的主体上的寄生蜂幼虫,试验劫持的代码测试,这样它会创建我们需要测试的确切条件。

此想法是作为XOR光标一样简单,所以我怀疑很多程序员都独立地发明了它。我发现它是与比赛条件测试的代码通常是有用的。我希望它能帮助。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top