1

I need to do the Unit Test of below code which is using countdownlatch. This is a test code only. I am using mockito thenAnswer and InvocationOnMask for mocking threads/callable. But I don't know how to initialize/mock or exit countdownlatch in unit testing.

public class ThreadPoolTaskExecRunner {
    private ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
    private ArrayBlockingQueue<String> values = new ArrayBlockingQueue<String>(100, true);

    public ThreadPoolTaskExecRunner() {
        threadPoolTaskExecutor.setCorePoolSize(5);
        threadPoolTaskExecutor.setMaxPoolSize(10);
        threadPoolTaskExecutor.setWaitForTasksToCompleteOnShutdown(true);
        threadPoolTaskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        threadPoolTaskExecutor.initialize();
    }

    public static void main(String args[]) {
        ThreadPoolTaskExecRunner obj = new ThreadPoolTaskExecRunner();
        obj.testCountDownLatch();
    }
    public void testCountDownLatch() {
        final CountDownLatch latch = new CountDownLatch(5);
        Future<String> future1 = threadPoolTaskExecutor.submit(new Task("A", values, latch));
        Future<String> future3 = threadPoolTaskExecutor.submit(new Task("B", values, latch));
        Future<String> future4 = threadPoolTaskExecutor.submit(new Task("C", values, latch));
        Future<String> future5 = threadPoolTaskExecutor.submit(new Task("D", values, latch));
        Future<String> future2 = threadPoolTaskExecutor.submit(new Task("E", values, latch));

        try{
            latch.await();  //main thread is waiting on CountDownLatch to finish
        }catch(InterruptedException ie){
            System.out.println(ie);
        }
        System.out.println("*********** DONE *********** values size= "+values.size());
        for(String s : values)
            System.out.println(s);
        threadPoolTaskExecutor.shutdown();
    }

    public static class Task implements Callable<String> {
        private String type;
        private ArrayBlockingQueue<String> values;
        private CountDownLatch latch;

        public Task(String s, ArrayBlockingQueue<String> values, CountDownLatch latch) {
            this.type = s;
            this.values = values;
            this.latch = latch;
        }

        @Override
        public String call() throws Exception {
            try {
                System.out.println("Inside call type: " + type);
                Thread.sleep(10);
                values.add(type);
                return type;
            } finally {
                if(latch != null)
                    latch.countDown();
            }
        }
    }
}

I developed the Unit Test class, But it is not helpful ...

@RunWith(MockitoJUnitRunner.class)
public class ThreadPoolTaskExecRunnerTest {
    @Mock
    private ThreadPoolTaskExecutor taskExecutor;

    @InjectMocks
    ThreadPoolTaskExecRunner threadPoolTaskExecRunner;

    @Test
    public void test() {
        when(taskExecutor.submit(any(ThreadPoolTaskExecRunner.Task.class))).thenAnswer(new Answer<Future<String>>() {
            public Future<String> answer(InvocationOnMock invocation) throws Throwable {
                Future<String> future = mock(FutureTask.class);
                when(future.isDone()).thenReturn(false, false, true);
                when(future.get()).thenReturn("This is a test");
                return future;
            }
        });

        threadPoolTaskExecRunner.testCountDownLatch();
    }
}
ganesh
  • 41
  • 2
  • 4
  • You shouldn't test code you didn't write... Did you ever write a unit-test to List/Array/Integer and etc ? Further, what exactly is it you're trying to test ? – Nir Alfasi May 31 '16 at 21:37
  • Looks like my method name testCountDownLatch() is confusing you. My service need to do some tasks in parallel and it should wait for all the tasks to be completed. I am using countdowlatch for that. Now I need to test my service. Please treat testCountDownLatch() method as my service method ... – ganesh May 31 '16 at 22:01

1 Answers1

4

Well what do you want to test? I suppose you want to test that countDown was called. So you can do it like this:

public void taskCallsCountDownOnce() {
    // setup
    final CountDownLatch latch = mock(CountDownLatch.class);

    // execution
    new Task("A", values, latch).call();

    // evaluation
    verify(latch).countDown();
}

If you also want to test that the value was added before calling countDown, then use:

public void taskAddsValueBeforeCallingCountDown() {
    // setup & execution
    // ...
    // evaluation
    InOrder inOrder = inOrder(latch, values);
    inOrder.verify(values).add(...);
    inOrder.verify(latch).countDown();
}

General notes:

  • In this particular case it would be easier to call Future.get() which will also wait until the task completed
  • Give your test methods descriptive names that help to understand what your are testing
  • Try to keep concurrency as far away from your unit test as practical. Most classes don't deal with concurrency themselves like your class Task. It does not care if it executed in a new Thread, it just happens to be in your case. So you can test this class separately. If you structure your code well you will have classes dealing with thread organization and nothing else and other classes that perform work and nothing else. The makes separating the test for algorithms and the test for thread handling easier and the tests simpler to read.
yankee
  • 38,872
  • 15
  • 103
  • 162