简体   繁体   中英

Mock method implementation Junit mockito

I am trying to test the following class:

public class PreprocessorServiceImpl implements PreprocessorService
{
private static final Logger LOG = LoggerFactory.getLogger( PreprocessorServiceImpl.class );


private FileServiceProvider fileServiceProvider;

private PreProcessorApiService preprocessorApiService;

private ScanProcessingHelper scanProcessingHelper;

private DocumentProcessingService docProcessingService;

private CacheTimeOutHandler cacheTimeHandler;
private DocumentEventLogger docEventLogger;
private ScanProcessingService scanProcessingService;
private EventLogger eventLogger;
private ScanTrackLogger scanTrackLogger;

//Getters and setters.

@Override
public void processDocument( KafkaDocumentMetaData kafkaDocumentMeta )
{
    APIKeyConfigurationVO apiConfigurationsVo = kafkaDocumentMeta.getCustomerConfiguration();
    DocumentRequest documentRequest = kafkaDocumentMeta.getDocumentRequest();

    LOG.info( "Started preprocessing for documentId: {}", documentRequest.getDocumentId() );
    PreprocessorApiResponse apiResponse;

    try {
        byte[] documentInBytes = getDocumentFileInBytes( documentRequest, apiConfigurationsVo ); //Download from cloud
        docEventLogger.logDocIdEvent( documentRequest.getDocumentId(), EventEnum.FILE_DOWNLOADED );
        apiResponse = preprocessorApiService.preProcess(
            documentRequest.getDocumentRequestAdditionalInfo().getPreProcessingModelUrl(), documentInBytes,
            documentRequest.getDocumentId() );
        documentRequest.setPreprocessorApiResponse( apiResponse );
    } catch ( Exception e ) {
        //If download / ML api failed unexpectedly, then mark it for failure
        handleFailure( documentRequest, apiConfigurationsVo, e );
        return;
    }

    //code to process further in case of success
}

private void handleFailure( DocumentRequest documentRequest, APIKeyConfigurationVO apiConfigurationsVo, Throwable thr )
{
    LOG.error( "Exception occured while processing document. marking it as failed.", thr );
    docProcessingService.persistAndModify( documentRequest, DocumentStatus.FAILED, cacheTimeHandler.getDocumentTimeOut() );//to redis
    docProcessingService.publishDocumentReqMeta( documentRequest, apiConfigurationsVo );//to result analyzer
    docEventLogger.logDocIdFailureEvent( documentRequest.getDocumentId(), thr );
}

}

There's a method that updates the state of an object inside docProcessingService.persistAndModify()

documentRequest.setStatus(documentStatus.getStatus());

as follows:

public void persistAndModify( DocumentRequest documentRequest, DocumentStatus documentStatus, int timeToLive )
{
    LOG.debug( "Persisting document request with document id {} and status {}", documentRequest.getDocumentId(),
        documentRequest.getStatus() );
    documentRequest.setStatus( documentStatus.getStatus() );
    persistResponse( documentRequest.getDocumentId(), documentRequest, timeToLive );
    LOG.info( "Updated document request and  status to: {} for documentId: {}", documentRequest.getStatus(),
        documentRequest.getDocumentId() );
}

I don't want to execute the complete method. But just set the status of documentRequest to documentStatus . Is this possible using junit mockito?

I tried using doAnswer but its throwing NPE. FOllwing is my code for the same.

Mockito.doAnswer( invocation -> {

        Object[] args = invocation.getArguments();
        DocumentRequest modified = ( (DocumentRequest) args[0] );
        modified.setStatus( DocumentStatus.FAILED.getStatus() );
        return null;

    } ).when( docProcessingService ).persistAndModify( any(), any(), any() );
    //.
    //.data population code
    //.
    assertEquals( docRequest.getStatus(), DocumentStatus.INITIATED.getStatus() );
    preprocessorService.processDocument( kafkaMessage );
    assertEquals( docRequest.getStatus(), DocumentStatus.FAILED.getStatus() );

It throws NPE at

} ).when( docProcessingService ).persistAndModify( any(), any(), any() );

I already have added-

@RunWith ( MockitoJUnitRunner.class) and

MockitoAnnotations.initMocks( PreprocessorServiceTests.class );

and mocked by using @Mock

 @Mock
private DocumentProcessingService docProcessingService;

Is doAnswer suitable for my use case or is there anything else ? How do I give a method implementation while mocking it?

Pardon if it is a silly question. I am new to using mockito.

EDIT: Adding the stacktrace

java.lang.NullPointerException
at my.package.service.PreprocessorServiceTests.testDocumentDownloadFailure(PreprocessorServiceTests.java:114)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
at org.mockito.internal.runners.DefaultInternalRunner$1$1.evaluate(DefaultInternalRunner.java:44)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.mockito.internal.runners.DefaultInternalRunner$1.run(DefaultInternalRunner.java:74)
at org.mockito.internal.runners.DefaultInternalRunner.run(DefaultInternalRunner.java:80)
at org.mockito.internal.runners.StrictRunner.run(StrictRunner.java:39)
at org.mockito.junit.MockitoJUnitRunner.run(MockitoJUnitRunner.java:163)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at org.junit.runner.JUnitCore.run(JUnitCore.java:115)
at org.junit.vintage.engine.execution.RunnerExecutor.execute(RunnerExecutor.java:40)
at java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:184)
at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193)
at java.util.Iterator.forEachRemaining(Iterator.java:116)
at java.util.Spliterators$IteratorSpliterator.forEachRemaining(Spliterators.java:1801)
at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481)
at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471)
at java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:151)
at java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:174)
at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
at java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:418)
at org.junit.vintage.engine.VintageTestEngine.executeAllChildren(VintageTestEngine.java:80)
at org.junit.vintage.engine.VintageTestEngine.execute(VintageTestEngine.java:71)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:220)
at org.junit.platform.launcher.core.DefaultLauncher.lambda$execute$6(DefaultLauncher.java:188)
at org.junit.platform.launcher.core.DefaultLauncher.withInterceptedStreams(DefaultLauncher.java:202)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:181)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:128)
at org.eclipse.jdt.internal.junit5.runner.JUnit5TestReference.run(JUnit5TestReference.java:89)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:41)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:541)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:763)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:463)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:209)

EDIT 2: I removed the doAnswer and found out the following two statements also throw NPE.

Mockito.doNothing().when( docProcessingService ).persistAndModify( any(), any(), any() );

verify( docProcessingService, times( 1 ) ).persistAndModify( any(), DocumentStatus.FAILED, any() );

So I think its something to do with void return type rather than doAnswer()

The following statement from same dependency works fine without any issues.

        when( docProcessingService.publishDocumentReqMeta( any(), any() ) ).thenReturn( true );

Following is the method signature for the same,

public boolean publishDocumentReqMeta( DocumentRequest docRequest, APIKeyConfigurationVO apiKeyConfiguration )

I don't want to execute the complete method. But just set the status of documentRequest to documentStatus. Is this possible using junit mockito?

No, it's not, because it's not what you want to do. You want to test that the method does everything it's supposed to, and that's both update the request status and persistResponse .

If you want to test them separately, create individual methods. You might also consider splitting the persist part into an entirely separate class, like this

class DocumentProcessingService {
    private Persistor persistor;
    public void persistAndModify( DocumentRequest documentRequest, DocumentStatus     documentStatus, int timeToLive ) {
        documentRequest.getStatus() );
        documentRequest.setStatus( documentStatus.getStatus() );
        persistor.persistResponse( documentRequest.getDocumentId(), documentRequest, timeToLive );
        documentRequest.getDocumentId() );
    }
}

you can then test this via

@RunWith(MockitoJUnitRunner.class)
class ProcessingServiceTest {
    @InjectMocks private DocumentProcessingService sut;
    @Mock private Persistor persistor;

    @Test
    public void testPersistAndModify() {
        DocumentRequest request = mock(DocumentRequest.class);
        when(request.getDocumentId()).thenReturn(id);
        DocumentStatus status = mock(DocumentStatus.class);
        when(status.getStatus).thenReturn(someStatus);

        sut.persistAndModify(request, status, x);

        // check status was updated
        verify(documentRequest).setStatus(someStatus);
        // check document was persisted
        verify(persistor).persistResponse(id, request, x);
    }
}

Chances are your actual persistResponse code does pretty much this, so you can verify that happened without refactoring.

EDIT:

after your edit, I'm not going to update the above code, you'll have to replace the appropriate code pieces yourself. In short, you'd have to mock the docEventLogger.logDocIdEvent and preprocessorApiService.preProcess calls.

The biggest problem is the "download from the cloud" part, you probably want to move that code into an external class as well so you can mock that download part as well.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM