0

I am new to multithreading and have the below sample code. How do we write unit test cases with max code coverage?

public class jobExecutorImpl<T> implements jobExecutor<T>
{

    private AcquisitionProcessor processor;
    private ExecutorService jobExecutor;
    private AcquisitionProcessExecutor executorService;

    @Override
    public AcquisitionResponse<T> processContent(Collection<T> itemsToProcess) throws Exception {
        AcquisitionResponse<T> acquisitionResponse = new AcquisitionResponse<>();
        if(CollectionUtils.isNotEmpty(itemsToProcess)){

          //  List<Callable<T>> tasks = new ArrayList<Callable<T>>();
           // CountDownLatch latch=new CountDownLatch(itemsToProcess.size());
            LOGGER.info("Items to Process: "+itemsToProcess.size());
            for (T item : itemsToProcess) {

                jobExecutor.execute(new Runnable() {
                    @Override
                    public void run() {
                        AcquisitionStatus status= null;
                        try {
                            status = executorService.process(item);
                        } catch (Exception e) {
                            e.printStackTrace();
                            //TODO catch filenotfound and throw
                        }
                        if(status!=null) {

                            acquisitionResponse.addStatus(item, status);
                        }
                    }

                });
              //  tasks.add(new threadExecutor(item,acquisitionResponse,latch));

            }
           // jobExecutor.invokeAll(tasks);


        }
        processor.archiveFile();
        return acquisitionResponse;
    }





// actually code processed in each thread

public class ProcessExecutor<T>
{

    private AcquisitionPreProcessor preProcessor;
    private AcquisitionProcessor processor;
    private Converter requestConverter;
    private Converter responseConverter;
    private MediaManagementService mediaManagementService;
    private ContentComparator contentComparator;
    private MediaContentDAO mediaContentDao;


     public AcquisitionStatus process(T item) throws Exception
    {
        LOGGER.debug("Processing for Id: " + item.toString()+"Thread: "+Thread.currentThread().getName());
        Object response = null;
        try {
            response = processor.processInternal(requestConverter.convert(item));
            List<MediaContent> targetList = ((SupplierContent) responseConverter.convert(response)).getMediaContents();
            if(CollectionUtils.isNotEmpty(targetList)){
                List<MediaContent> sourceList = mediaContentDao.getAllParentMediaContentsByLobTypeAndValue(targetList.get(0).getContentLobValue(),  targetList.get(0).getContentLOBType());
                List<MediaContent> listOfComparedMediaContents = contentComparator.compare(sourceList,targetList);
                int insertCount = 0,deleteCount = 0;
                for(MediaContent mediaContent:listOfComparedMediaContents){
                    if(mediaContent.getActionType().equals(ActionType.DELETE)){
                        LOGGER.info("Processing delete");
                        mediaManagementService.deleteContent(mediaContent);
                        deleteCount = deleteCount + getNumOfMediaContents(mediaContent);
                    }
                    if(mediaContent.getActionType().equals(ActionType.INSERT)){
                        LOGGER.info("Processing insert");
                        mediaManagementService.insertContent(mediaContent);
                        insertCount = insertCount + getNumOfMediaContents(mediaContent);
                    }
                }
                if(deleteCount + insertCount > 0){
                   return new AcquisitionStatus(StatusType.Processed, insertCount, deleteCount);
                }
            }
        }catch(FileNotFoundException e)
        {
            throw e;
        }
        catch(Exception e) {

            handleException(item, e);
            return new AcquisitionStatus(StatusType.Error, e);
        }
        return null;
    }
Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
  • You should follow the Java conventions and begin class and interface names with a capital letter. Thus `JobExecutorImpl` not `jobExecutorImpl`. – Raedwald Nov 06 '18 at 13:29

3 Answers3

0

Anonymous classes are hard to unit test. If a code part has a minimum complexity I do not use anonymous classes for that reasons:

see your code:

jobExecutor.execute(new Runnable() {

This new Runnable() is a problem if you want to conver this. Instead to define an anonymous class you could define a normal class, e.g an Inner class (e.g AquisitionExecutor) and pass a new instance to execute. Howver in your case this seems not neccessary. The anonymous class in your case is not so omplex. I could live that it will not be convered by a unit test.

MultiThreaded code is not easy to unit test. Make sure the unit test does not terminate before the threads terminate. And check the expected results after the threads finished their work.

I do that by defining an ITarget interface which is used by the code to forward their calculation result. In case of an unit test, I replace that target instance with the testCaseClass which implements ITarget, too.

AlexWien
  • 28,470
  • 6
  • 53
  • 83
0
public class JobExecutorImplTest {
    @Test
    public void testItemsProcessedConcurrently() {
        /*
        1) There is a CountDownLatch processingStarted = new CountDownLatch(2);
        2) initialize jobExecutorImpl with:
            - fixed-thread pool (2 threads) Executors.newFixedThreadPool(2)
            - MockExecutorService
        3) MockExecutorService does following in it's process method:
            - processingStarted.countDown();
            - processingStarted.await()
        4) Run processing: jobExecutorImpl.processContent()
        5) wait in test that processing of all items has been started: processingStarted.await()
        */
    } 
}
jnr
  • 790
  • 1
  • 7
  • 9
0
  1. Change you class so, instead of containing an ExecutorService, its constructor uses Dependency Injection to be given the ExecutorService to use.
  2. Change your class so operations that submit a task to the ExectorService return the Future for that task.
  3. In your unit tests, use a simple single-threaded ExecutorService and Future.get() the task results. You can then test the correctness of your own code without having to worry about thread safety and simultaneous task execution.
Raedwald
  • 46,613
  • 43
  • 151
  • 237