有没有办法测试方法处理并发调用的能力?

时间:2016-01-28 21:07:39

标签: java unit-testing junit

我有一个在数据库表中创建实体的方法。我想测试方法在同一时间被调用两次的情况。

该方法具有避免这种情况的逻辑,并且失败了2个请求中的一个,但我无法确认这是否正常工作。

有没有办法在不测试片状的情况下测试这个?

我粗暴地试图从2个浏览器同时调用该动作,但它们不能并行执行。我想这将需要大量的试验和错误才能让它们在同一时间执行(它是在tomcat上运行的war文件)

2 个答案:

答案 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),但它们会强化您的代码并且可能会出现并发问题。