Search code examples
javamockitorx-java2junit5

Test an overridden method of an inner class with JUnit and Mockito


I have a method in a class I need to test. The method uses an external class that I need to mock, so the external class doesn't get tested or executes its dependencies. The special challenge is: one method of the external class gets overridden. Method looks like this:

public void fetchLocalData(final String source, final ObservableEmitter<String> destination) {
   final List<String> options = Arrays.asList("recursive","allFiles","includeDir");
   // This class comes from a package
   final DirScan dirscan = new DirScan(source, options) {
       @Override
       protected Action getResult(final String result) {
           destination.onNext(result);
           return Action.Continue;
       }
   };
   dirscan.scan();
   destination.onComplete();
}

I tried:

    DirScan scanner = mock(DirScan.class);
    when(scanner.scan()).thenReturn("one").thenReturn("two");

That didn't work. What do I miss? How would I need to refactor to make this testable?


Solution

  • If you want to replace the dirscan with a mock (or a spy) you'll need to refactor your class that it's a dependency or parameter. Alternatively you could use PowerMockito's whenNew functionality.

    Lets assume you change your class and instead of the String source you provide the DirScan object as a parameter. You would need to have some kind of creation method for dirscan elsewhere (might be a static method).

    final List<String> options = Arrays.asList("recursive","allFiles","includeDir");
    
    public DirScan createDirScan(String source) {
    
       // This class comes from a package
       final DirScan dirscan = new DirScan(source, options) {
           @Override
           protected Action getResult(final String result) {
               destination.onNext(result);
               return Action.Continue;
           }
       };
    
       return dirscan;
    }
    
    public void fetchLocalData(final DirScan dirscan, final ObservableEmitter<String> destination) {
        dirscan.scan();
        destination.onComplete();
    }
    

    Juding from your question you seem to want to test the interaction with the destination object, so you do not want to mock the dirscan object (because if you do there won't be any interaction). You might want to use a spy and only replace the getResult method.

    In your test now you could then simply pass a spy for the dirscan object and define the behaviour of it with thenAnswer.

    final ObservableEmitter<String> destination = ...
    
    DirScan dirscan = Mockito.spy(createDirScan(source, destination));
    
    Mockito.when(dirscan.getResult(Mockito.any(String.class))).thenAnswer((Answer<Action>) invocation -> {
        String result = invocation.getArgument(0);
        destination.onNext(result);
    
        return Action.Continue;
    });
    
    classUnderTest.fetchLocalData(dirscan, destination);
    

    At this point you might notice that its probably better to not use a spy and just use the real DirScan object. Using the spy to do what you intend to do with the overriden method looks like overkill to me.

    The real object has to work for this test to be of value, so you might as well test the real thing.