ChatGPT解决这个技术问题 Extra ChatGPT

How to mock void methods with Mockito

How to mock methods with void return type?

I implemented an observer pattern but I can't mock it with Mockito because I don't know how.

And I tried to find an example on the Internet but didn't succeed.

My class looks like this:

public class World {

    List<Listener> listeners;

    void addListener(Listener item) {
        listeners.add(item);
    }

    void doAction(Action goal,Object obj) {
        setState("i received");
        goal.doAction(obj);
        setState("i finished");
    }

    private string state;
    //setter getter state
} 

public class WorldTest implements Listener {

    @Test public void word{
    World  w= mock(World.class);
    w.addListener(this);
    ...
    ...

    }
}

interface Listener {
    void doAction();
}

The system is not triggered with mock.

I want to show the above-mentioned system state. And make assertions according to them.

Beware that void methods on mocks do nothing by default!
@Line, that is what I was looking for. It seems obvious after you say it. But it does highlight a mocking principle: You only need to mock methods of mocked classes for their effects, like a return value or an exception. Thank you!

A
Ahmed Ashour

Take a look at the Mockito API docs. As the linked document mentions (Point # 12) you can use any of the doThrow(),doAnswer(),doNothing(),doReturn() family of methods from Mockito framework to mock void methods.

For example,

Mockito.doThrow(new Exception()).when(instance).methodName();

or if you want to combine it with follow-up behavior,

Mockito.doThrow(new Exception()).doNothing().when(instance).methodName();

Presuming that you are looking at mocking the setter setState(String s) in the class World below is the code uses doAnswer method to mock the setState.

World mockWorld = mock(World.class); 
doAnswer(new Answer<Void>() {
    public Void answer(InvocationOnMock invocation) {
      Object[] args = invocation.getArguments();
      System.out.println("called with arguments: " + Arrays.toString(args));
      return null;
    }
}).when(mockWorld).setState(anyString());

@qualidafial:Yeah, I think parameterization to Void would be better as it better conveys that I am not interested in the return type. I wasn't aware of this construct, thanks for pointing it out.
doThrow is #5 now (also for me using doThrow this fixed the message "'void' type not allowed here", for followers...)
@qualidafial: I think the return type of the Answer.answer call is not what gets returned to the original method, it's what is returned to the doAnswer call, presumably if you want to do something else with that value in your test.
:( in trying to Mock version 16.0.1 of RateLimiter.java in guava doNothing().when(mockLimiterReject).setRate(100) results in calling teh setRate of the RateLimiter resulting in nullpointer since mutex is null for some reason once mockito bytecoded it so it did not mock my setRate method :( but instead called it :(
@DeanHiller notice that setRate() is final, and therefore cannot be mocked. Instead try create()-ing an instance that does what you need. There should be no need to mock RateLimiter.
M
MarcioB

I think I've found a simpler answer to that question, to call the real method for just one method (even if it has a void return) you can do this:

Mockito.doCallRealMethod().when(<objectInstance>).<method>();
<objectInstance>.<method>();

Or, you could call the real method for all methods of that class, doing this:

<Object> <objectInstance> = mock(<Object>.class, Mockito.CALLS_REAL_METHODS);

This is the real answer right here. The spy() method works fine, but generally is reserved for when you want the object to do most everything normally.
What does this mean? Are you actually calling the methods? I haven't really used mockito before.
Yes, the mock will call the real methods. If you use the @Mock you can specify the same with: @Mock(answer = Answers.CALLS_REAL_METHODS) to obtain the same results.
If you are calling an abstract method to achieve runtime polymorphism, then replace .doCallRealMethod() with doNothing()
A
Ahmed Ashour

Adding to what @sateesh said, when you just want to mock a void method in order to prevent the test from calling it, you could use a Spy this way:

World world = new World();
World spy = Mockito.spy(world);
Mockito.doNothing().when(spy).methodToMock();

When you want to run your test, make sure you call the method in test on the spy object and not on the world object. For example:

assertEquals(0, spy.methodToTestThatShouldReturnZero());

P
Peter Coulton

The solution of so-called problem is to use a spy Mockito.spy(...) instead of a mock Mockito.mock(..).

Spy enables us to partial mocking. Mockito is good at this matter. Because you have class which is not complete, in this way you mock some required place in this class.


I stumbled in here because I had a similar problem (also, coincidentally, happened to be testing an Subject/Observer interaction). I'm already using a spy but I want the 'SubjectChanged' method to do something different. I could use `verify(observer).subjectChanged(subject) just to see that the method was called. But, for some reason, I'd much rather override the method. For that, a combination of Sateesh's approach and your answer here was the way to go...
No, doing this won't actually help with mocking void methods. The trick is to use one of the four Mockito static methods listed in sateesh's answer.
@Gurnard for your question take a look at this stackoverflow.com/questions/1087339/….
f
fl0w

First of all: you should always import mockito static, this way the code will be much more readable (and intuitive):

import static org.mockito.Mockito.*;

For partial mocking and still keeping original functionality on the rest mockito offers "Spy".

You can use it as follows:

private World world = spy(new World());

To eliminate a method from being executed you could use something like this:

doNothing().when(someObject).someMethod(anyObject());

to give some custom behaviour to a method use "when" with an "thenReturn":

doReturn("something").when(this.world).someMethod(anyObject());

For more examples please find the excellent mockito samples in the doc.


D
Dilini Rajapaksha

How to mock void methods with mockito - there are two options:

doAnswer - If we want our mocked void method to do something (mock the behavior despite being void). doThrow - Then there is Mockito.doThrow() if you want to throw an exception from the mocked void method.

Following is an example of how to use it (not an ideal usecase but just wanted to illustrate the basic usage).

@Test
public void testUpdate() {

    doAnswer(new Answer<Void>() {

        @Override
        public Void answer(InvocationOnMock invocation) throws Throwable {
            Object[] arguments = invocation.getArguments();
            if (arguments != null && arguments.length > 1 && arguments[0] != null && arguments[1] != null) {

                Customer customer = (Customer) arguments[0];
                String email = (String) arguments[1];
                customer.setEmail(email);

            }
            return null;
        }
    }).when(daoMock).updateEmail(any(Customer.class), any(String.class));

    // calling the method under test
    Customer customer = service.changeEmail("old@test.com", "new@test.com");

    //some asserts
    assertThat(customer, is(notNullValue()));
    assertThat(customer.getEmail(), is(equalTo("new@test.com")));

}

@Test(expected = RuntimeException.class)
public void testUpdate_throwsException() {

    doThrow(RuntimeException.class).when(daoMock).updateEmail(any(Customer.class), any(String.class));

    // calling the method under test
    Customer customer = service.changeEmail("old@test.com", "new@test.com");

}
}

You could find more details on how to mock and test void methods with Mockito in my post How to mock with Mockito (A comprehensive guide with examples)


Great Example. Note: In java 8, it might be a little nicer to use a lambda instead instead of an annonymous class: 'doAnswer((Answer) invocation -> { //CODE }).when(mockInstance).add(method());'
T
Tim B

In Java 8 this can be made a little cleaner, assuming you have a static import for org.mockito.Mockito.doAnswer:

doAnswer(i -> {
  // Do stuff with i.getArguments() here
  return null;
}).when(*mock*).*method*(*methodArguments*);

The return null; is important and without it the compile will fail with some fairly obscure errors as it won't be able to find a suitable override for doAnswer.

For example an ExecutorService that just immediately executes any Runnable passed to execute() could be implemented using:

doAnswer(i -> {
  ((Runnable) i.getArguments()[0]).run();
  return null;
}).when(executor).execute(any());

In one line: Mockito.doAnswer((i) -> null).when(instance).method(any());
@AkshayThorve That doesn't work when you actually want to do stuff with i though.
p
poussma

Adding another answer to the bunch (no pun intended)...

You do need to call the doAnswer method if you can't\don't want to use spy's. However, you don't necessarily need to roll your own Answer. There are several default implementations. Notably, CallsRealMethods.

In practice, it looks something like this:

doAnswer(new CallsRealMethods()).when(mock)
        .voidMethod(any(SomeParamClass.class));

Or:

doAnswer(Answers.CALLS_REAL_METHODS.get()).when(mock)
        .voidMethod(any(SomeParamClass.class));

a
ashley

I think your problems are due to your test structure. I've found it difficult to mix mocking with the traditional method of implementing interfaces in the test class (as you've done here).

If you implement the listener as a Mock you can then verify the interaction.

Listener listener = mock(Listener.class);
w.addListener(listener);
world.doAction(..);
verify(listener).doAction();

This should satisfy you that the 'World' is doing the right thing.


K
Kıvılcım

If you need to do some operations in the mocked void method, and you need to manipulate the argument that sent to void method; you can combine Mockito.doAnswer with ArgumentCaptor.capture method.

Let's say you have SpaceService that autowires a GalaxyService, which has a void method called someServiceMethod.

You want to write test for one of your method in SpaceService that calls GalaxyService's void method. Your planet is also generated inside SpaceService. So you don't have any chance to mock that.

Here is your sample SpaceService class that you want to write tests for.

class SpaceService {
    @Autowired
    private GalaxyService galaxyService;

    public Date someCoolSpaceServiceMethod() {
        // does something

        Planet planet = new World();
        galaxyService.someServiceMethod(planet); //Planet updated in this method.

        return planet.getCurrentTime();
    }
}

The GalaxyService.someServiceMethod method expects a planet argument. Does some stuff in the method. See :

GalaxyService {
    public void someServiceMethod(Planet planet) {
        //do fancy stuff here. about solar system etc.

        planet.setTime(someCalculatedTime); // the thing that we want to test.

        // some more stuff.
    }
}

And you want to test this feature.

Here is an example :

ArgumentCaptor<World> worldCaptor = ArgumentCaptor.forClass(World.class);
Date testDate = new Date();

Mockito.doAnswer(mocked-> {
    World capturedWorld = worldCaptor.getValue();
    world.updateTime(testDate);
    return null;
}).when(galaxyService.someServiceMethod(worldCaptor.capture());

Date result = spaceService.someCoolSpaceServiceMethod();

assertEquals(result, testDate);

N
Nikita

In your example you should mock Listener item and use Mockito.verify to check interactions with it