我有一个在数据库表中创建实体的方法。我想测试方法在同一时间被调用两次的情况。
该方法具有避免这种情况的逻辑,并且失败了2个请求中的一个,但我无法确认这是否正常工作。
有没有办法在不测试片状的情况下测试这个?
我粗暴地试图从2个浏览器同时调用该动作,但它们不能并行执行。我想这将需要大量的试验和错误才能让它们在同一时间执行(它是在tomcat上运行的war文件)
答案 0 :(得分:1)
我最近遇到了类似的问题。这是我为解决这个特定问题所写的确切代码:
@Test
public void mapVertexToLazyObjectWithSameMapperOnMultipleThreadsAtTheSameTime() {
StackVertex vertex = new StackVertex(graph, 1L, "test", Optional.empty());
mockVertex(vertex, NO_EDGES, NO_EDGES, properties("name", "foobar"));
VertexEntityMapper<NamedNode> mapper = objectMapper.getMapper(NamedNode.class);
NamedNode obj = mapper.mapToObject(vertex);
final Boolean[] flags = { false, false };
Runnable run = () -> {
if ( obj.getName() == null || !obj.getName().equals("foobar") ) {
flags[0] = true;
}
};
List<Thread> threads = IntStream.range(0, 10).boxed()
.map(i -> new Thread(run)).collect(toList());
threads.forEach(Thread::start);
threads.forEach((thread) -> {
try { thread.join(); } catch (InterruptedException e) {
flags[1] = true;
}
});
verify(vertexRepository, times(1)).findById(graph.tx(), 1L, Optional.empty());
assertFalse("race condition", flags[0]);
assertFalse("thread interrupted", flags[1]);
}
它非常简单,但效果很好。如果我使代码线程不安全,这个测试很容易发现它的问题。我玩了一些线程数,但发现有10个线程我总能重现竞争条件或线程不安全抛出的异常。
答案 1 :(得分:1)
测试测试。
获取应该是线程安全的方法的副本,并使复制线程不安全。编写一个单元测试,用一个runnable激活十几个线程:
1)共享CountDownLatch。
2)等待CountDownLatch达到零。
3)调用线程不安全的方法。
使用CountDownLatch,您可以在或多或少同时触发所有线程调用线程不安全方法(当CountDownLatch达到零时,所有线程都准备好从runnable中的相同点开始,但最终它已经启动到你的操作系统(和硬件)来决定什么时候执行)。 评估结果:您现在应该看到差异(例如插入了11条记录而不是您预期的12条记录)。
重复测试或将测试置于for循环中(并将CountDownLatch替换为CyclicBarrier,这是另一个可用于此类测试的并发工具)。在任何情况下,请确保您的测试始终显示不需要的结果(例如,永远不要假设线程同时启动,将它们与CountDownLatch等工具同步,以便您确切知道线程的位置。)
在单元测试中调用线程安全方法替换对线程不安全方法的调用。您现在应该看到期望和预期的结果而没有差异。
我在这里没有提供太多细节,但问题中也没有太多细节。在任何情况下,一般的想法是首先设置一个应该出错的情况并使用自动暴力(许多线程在循环中多次调用该方法)结合智能使用并发工具(如CountDownLatch)来显示测试总是会带来某些问题。 “智能使用并发工具”需要一些时间,洞察力和实践(例如,我花了一些时间来确定在调用“thread.start()”后,线程可能尚未启动并且可以使用CountDownLatch确保线程是我想要的地方。)
免责声明:这些“经过测试”的测试不会捕获所有并发问题(您只测试您认为可能失败的问题,还有其他问题,例如已损坏的Double checked locking),但它们会强化您的代码并且可能会出现并发问题。