How do I write a JUnit test case to test threads and events
Asked Answered
F

7

19

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.

Fatso answered 17/3, 2011 at 7:47 Comment(0)
J
12

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.

Janijania answered 17/3, 2011 at 8:28 Comment(0)
M
6

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.

Mcglynn answered 20/6, 2011 at 21:59 Comment(0)
B
3

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.

Bearden answered 17/3, 2011 at 8:23 Comment(2)
So are you saying that multi-threaded code tests cannot be automated?Warfield
you can write the tests and they can work, but in my experience multithreaded tests are fragile and they don't protect you against timing issues.Bearden
M
3

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();
    }
}
Myriagram answered 22/8, 2019 at 12:20 Comment(0)
I
2

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

Intermediate answered 17/3, 2011 at 8:9 Comment(0)
F
2

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.

Forced answered 17/3, 2011 at 9:6 Comment(0)
P
1

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();
   }
}
Perianth answered 16/9, 2016 at 7:12 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.