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

发布于 2024-10-22 06:43:08 字数 155 浏览 5 评论 0原文

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

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

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

I have a java code which works in one (main) thread. From the main thread, i spawn a new thread in which I make a server call. After the server call is done, I am doing some work in the new thread and after that the code joins the main thread.

I am using eclipse Jobs to do the server call.

I want to know, how do I write a JUnit test case for this.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

凉栀 2024-10-29 06:43:08

您可能需要重组代码以便可以轻松测试。

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

  1. 线程管理代码:启动线程并可能等待结果的代码
  2. 在线程中运行的“工作者”代码
  3. 当多个线程处于活动状态时可能导致的并发问题

构建您的实现这样您的线程管理代码就无法了解 Worker 的详细信息。然后,您可以使用 Mock Workers 来启用线程管理测试 - 例如,以某些方式失败的 Mock Worker 允许您测试管理代码中的某些路径。

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

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

You may need to restructure your code so that it can be easily tested.

I can see several distinct areas for testing:

  1. Thread Management code: the code that launches the thread(s) and perhaps waits for results
  2. The "worker" code run in the thread
  3. The concurrency issues that may result when multiple threads are active

Structure your implementation so that Your Thread Management code is agnostic as to the details of the Worker. Then you can use Mock Workers to enable testing of Thread Management - for example a Mock Worker that fails in certain ways allows you to test certain paths in the management code.

Implement the Worker code so that it can be run in isolation. You can then unit test this independently, using mocks for the server.

For concurrency testing the links provided by Abhijeet Kashnia will help.

神也荒唐 2024-10-29 06:43:08

这就是我创建 ConcurrentUnit 的目的。一般用法是:

  1. 生成一些线程
  2. 让主线程等待或睡眠
  3. 从工作线程内执行断言(通过 ConcurrentUnit,报告回主线程)
  4. 一旦所有断言完成,就从工作线程之一恢复主线程

有关详细信息,请参阅 ConcurrentUnit 页面。

This is what I created ConcurrentUnit for. The general usage is:

  1. Spawn some threads
  2. Have the main thread wait or sleep
  3. Perform assertions from within the worker threads (which via ConcurrentUnit, are reported back to the main thread)
  4. Resume the main thread from one of the worker threads once all assertions are complete

See the ConcurrentUnit page for more info.

顾北清歌寒 2024-10-29 06:43:08

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

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

The resources provided by Abhijeet Kashnia may help, but I am not sure what you are trying to achieve.

You can do unit testing with mocks to verify your code, that won't test concurrency but will provide coverage.
You can write an integration test to verify that the threads are being created and joined in the fashion you expect.However this will not guarantee against concurrency problems. Most concurrent problems are caused by timing bugs which are not predictable and thus can't be tested for accurately.

霓裳挽歌倾城醉 2024-10-29 06:43:08

当您唯一的问题是等待结果时,请使用 ExecutorService 用于生成线程。它可以接受工作作业作为 Runnable可调用。当您使用后者时,您将获得 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();
    }
}

When your only problem is waiting for the result, use ExecutorService for spawning your threads. It can accept work jobs both as Runnable and Callable. When you use the latter, then you are given a Future object in return, that can be used to wait for the result. You should consider using ExecutorService anyway, as from what I understand, you create many threads, and this is a perfect use case for executor services.

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;
    }
}

And the test class:

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();
    }
}
一个人的夜不怕黑 2024-10-29 06:43:08

我建议您使用模拟框架来确认确实进行了服务器调用。至于线程单元测试:对多线程应用程序进行单元测试

I suggest you use a mocking framework, to confirm that the server call was indeed made. As for the thread unit testing: Unit testing multithreaded applications

温柔戏命师 2024-10-29 06:43:08

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

测试线程的困难之一来自于它们的本质——它们是并发的。这意味着您被迫编写 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();
    }
}

我真的不喜欢这样做,更喜欢模拟并同意其他答案。但是,如果您需要测试线程,那么这是我认为有效的一种方法。

I'm guessing that you may have done your mocking code and may want a simple integration test to ensure that that your server call works.

One of the difficulties in testing threads comes from their very nature - they're concurrent. This means that you're force into writing JUnit test code that is forced to wait until your thread has finished its job before testing your code's results. This isn't a very good way of testing code, and can be unreliable, but usually means that you have some idea about whether you code is working.

As an example, your code may look something like:

@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();
    }
}

I really don't like doing this and prefer mocks and agree with the other answers. But, if you need to test your threads, then this is one approach that I find works.

栀梦 2024-10-29 06:43:08

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

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();
   }
}

Here is my solution to test asynchrone method which used thread.start:

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();
   }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文