如何编写JUnit测试用例来测试线程和事件

时间:2011-03-17 07:47:27

标签: java multithreading events junit rcp

我有一个在一个(主)线程中工作的java代码。从主线程,我产生一个新的线程,我在其中进行服务器调用。服务器调用完成后,我在新线程中做了一些工作,然后代码加入主线程。

我正在使用eclipse Jobs来进行服务器调用。

我想知道,我如何为此编写JUnit测试用例。

7 个答案:

答案 0 :(得分:8)

您可能需要重新构建代码,以便轻松测试。

我可以看到几个不同的测试区域:

  1. 线程管理代码:启动线程并可能等待结果的代码
  2. “worker”代码在线程
  3. 中运行
  4. 多线程处于活动状态时可能导致的并发问题
  5. 构建您的实现,以便您的线程管理代码与Worker的详细信息无关。然后,您可以使用Mock Workers来启用线程管理测试 - 例如,以某种方式失败的Mock Worker允许您测试管理代码中的某些路径。

    实现Worker代码,以便它可以独立运行。然后,您可以使用服务器的模拟单独对此进行单元测试。

    对于并发测试,Abhijeet Kashnia提供的链接将有所帮助。

答案 1 :(得分:5)

这是为ConcurrentUnit创建的。一般用法是:

  1. 产生一些线程
  2. 主线程等待或休眠
  3. 从工作线程中执行断言(通过ConcurrentUnit报告回主线程)
  4. 所有断言完成后,从其中一个工作线程恢复主线程
  5. 有关详细信息,请参阅ConcurrentUnit页面。

答案 2 :(得分:2)

我建议您使用模拟框架,以确认服务器调用确实已经完成。至于线程单元测试:Unit testing multithreaded applications

答案 3 :(得分:2)

Abhijeet Kashnia提供的资源可能有所帮助,但我不确定您要实现的目标。

您可以使用模拟进行单元测试来验证您的代码,这些代码不会测试并发性,但会提供覆盖率。 您可以编写集成测试来验证是否以您期望的方式创建和加入线程。但是,这不能保证不会出现并发问题。大多数并发问题是由时间错误引起的,这些错误是无法预测的,因此无法准确测试。

答案 4 :(得分:1)

我猜你可能已经完成了你的模拟代码,可能需要一个简单的集成测试来确保你的服务器调用工作。

测试线程的一个难点来自于它们的本质 - 它们是并发的。这意味着您需要编写JUnit测试代码,在测试代码结果之前,该代码必须等到线程完成其工作。这不是测试代码的一种非常好的方法,并且可能不可靠,但通常意味着您对代码是否正常工作有所了解。

例如,您的代码可能类似于:

@Test
public void myIntegrationTest() throws Exception {

   // Setup your test


   // call your threading code
   Results result = myServerClient.doThreadedCode();

   // Wait for your code to complete
   sleep(5);

   // Test the results
   assertEquals("some value",result.getSomeValue());

}


private void sleep(int seconds) {

    try {
        TimeUnit.SECONDS.sleep(seconds);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

我真的不喜欢这样做,喜欢嘲笑并同意其他答案。但是,如果你需要测试你的线程,那么这是我发现的一种方法。

答案 5 :(得分:0)

这是我使用thread.start测试asynchrone方法的解决方案:

public class MyClass {      
   public void doSomthingAsynchrone() {
      new Thread(() -> {
         doSomthing();
      }).start();
   }

   private void doSomthing() {
   }
}

@RunWith(PowerMockRunner.class)
@PrepareForTest(MyClass.class)
public class MyClassTest {
   ArgumentCaptor<Runnable> runnables = ArgumentCaptor.forClass(Runnable.class);

   @InjectMocks
   private MyClass myClass;

   @Test
   public void shouldDoSomthingAsynchrone() throws Exception {  

      // create a mock for Thread.class
      Thread mock = Mockito.mock(Thread.class);

      // mock the 'new Thread', return the mock and capture the given runnable
      whenNew(Thread.class).withParameterTypes(Runnable.class)
            .withArguments(runnables.capture()).thenReturn(mock);

      myClass.doSomthingAsynchrone();

      runnables.getValue().run();

      /**
       *  instead of 'runnables.getValue().run();' you can use a real thread.start
       *
       *   MockRepository.remove(Thread.class);
       *   Thread thread = new Thread(runnables.getValue());
       *   thread.start();
       *   thread.join();
       **/

      verify(myClass, times(1)).doSomthing();
   }
}

答案 6 :(得分:0)

当唯一的问题是等待结果时,请使用ExecutorService生成线程。它可以同时接受RunnableCallable的工作作业。当使用后者时,将为您提供一个Future对象作为回报,该对象可用于等待结果。无论如何,您都应该考虑使用 ExecutorService ,据我了解,您创建了许多线程,这是执行程序服务的完美用例。

class AnyClass {
    private ExecutorService threadPool = Executors.newFixedThreadPool(5);

    public List<Future<Integer>> anyMethod() {
        List<Future> futures = new ArrayList<>();

        futures.add(threadPool.submit(() -> {
            // Do your job here
            return anyStatusCode;
        }));

        futures.add(threadPool.submit(() -> {
            // Do your other job here
            return anyStatusCode;
        }));

        return futures;
    }
}

测试类:

class TestAnyClass {
    @Test
    public void testAnyMethod() {
        AnyClass anyObject = new AnyClass();

        List<Future<Integer>> futures = anyObject.anyMethod();
        CompletableFuture[] completable = futures.toArray(new CompletableFuture[futures.size()]);

        // Wait for all
        CompletableFuture.allOf(completable).join();
    }
}