Make mockito throw exception in case the stub is not defined [duplicate] - java

This question already has answers here:
How do I get Mockito mocks to cause a failure when unexpected calls are made?
(4 answers)
Closed 1 year ago.
I'm trying to understand if I can do the following in mockito.
Let's say I have this class:
class PersonService {
public NameResolver resolver;
public String getFullName(Person person) {
// ...
String firstName = resolver.getFirstName(person);
// ...
String lastName = resolver.getLastName(person);
// ...
return firstName + lastName;
}
}
And have a test for this class:
class PersonServiceTest {
private PersonService underTest;
#Test
void test() {
NameResolver mock = mock(NameResolver.class);
PersonService underTest = new PersonService(mock);
when(mock.getFirstName()).thenReturn("first-name");
underTest.getFullName();
}
}
The thing is, that I have unstabbed resolver.getLastName(person) method and mockito will stub this with default value ("" in case of String, I guess). What I'd like to do is to throw exception every time I call method of the mock that no stub attached. There is a framework named mockk in Kotlin ecosystem which behaves like this and this feature makes you write really reliable unit tests and it is pretty convinient after all.
I saw Answers class in mockito and it looks like this behaviour is configurable but I spend pretty much time looking for an answer on the web and suprisingly found nothing.
Moreover I saw that I can make mockito throw exceptions in case there are mocks that are not in use, but didn't found if I can make this option a default one, like via config file or something like this.

Create the mock with a default answer:
NameResolver mock =
mock(
NameResolver.class,
invocation -> { throw new UnsupportedOperationException(); });
This answer will only be invoked for otherwise-unstubbed methods.

Related

Mockito WrongTypeOfReturnValue: Boolean cannot be returned by findById()

I am trying to test the following method with JUnit tests, using Mockito:
#Override public List<Adoption> search(String username, Integer id) {
List<Adoption> emptySearchResult = new ArrayList<>();
if(id != null && !username.equals("") ) {
if(!this.petRepository.findById(id).isPresent()){
return emptySearchResult;
}
if(!this.appUserRepository.findByUsername(username).isPresent()){
return emptySearchResult;
}
Pet pet = this.petRepository.findById(id).orElseThrow( () -> new PetNotFoundException(id));
AppUser user = this.appUserRepository.findByUsername(username).orElseThrow( () -> new UsernameNotFoundException(username));
return this.adoptionRepository.findAllByUserAndPet(user, pet);
}
else if(id != null && username.equals("")){
if(!this.petRepository.findById(id).isPresent()){
return emptySearchResult;
}
Pet pet = this.petRepository.findById(id).orElseThrow( () -> new PetNotFoundException(id));
return this.adoptionRepository.findAllByPet(pet);
}
else if(id == null && !username.equals("")) {
if(!this.appUserRepository.findByUsername(username).isPresent()){
return emptySearchResult;
}
AppUser user = this.appUserRepository.findByUsername(username).orElseThrow( () -> new UsernameNotFoundException(username));
return this.adoptionRepository.findAllByUser(user);
}
else {
return this.adoptionRepository.findAll();
}
}
However, I run into a problem with the following part:
if(!this.petRepository.findById(id).isPresent())
Even though I have mocked this.petRepository.findById(id), for some reason isPresent() is returning false. This is my initialization for the tests:
#Mock
private AdoptionRepository adoptionRepository;
#Mock
private PetRepository petRepository;
#Mock
private AppUserRepository appUserRepository;
private AdoptionServiceImpl service;
private Adoption adoption1;
private Adoption adoption2;
private Adoption adoption3;
private AppUser user;
private AppUser user2;
private Pet pet;
private Pet petAlteadyAdopted;
List<Adoption> allAdoptions = new ArrayList<>();
List<Adoption> userFilteredAdoptions = new ArrayList<>();
List<Adoption> petFilteredAdoptions = new ArrayList<>();
#Before
public void init() {
MockitoAnnotations.initMocks(this);
user = new AppUser("username","name","lastname","email#gmail.com","pass",ZonedDateTime.now(), Role.ROLE_USER, City.Skopje);
user2 = new AppUser("username1","name","lastname","email#gmail.com","pass",ZonedDateTime.now(), Role.ROLE_USER, City.Skopje);
Center center = new Center("a", City.Bitola,"url");
pet = new Pet("p", Type.DOG,"b", Gender.FEMALE,"d",center, ZonedDateTime.now(),"url",null,false,ZonedDateTime.now());
petAlteadyAdopted = new Pet("p", Type.DOG,"b", Gender.FEMALE,"d",center, ZonedDateTime.now(),"url",null,true,ZonedDateTime.now());
pet.setId(0);
petAlteadyAdopted.setId(1);
adoption1 = new Adoption(ZonedDateTime.now(),ZonedDateTime.now(),Status.ACTIVE,user,pet);
adoption2 = new Adoption(ZonedDateTime.now(),ZonedDateTime.now(),Status.CLOSED,user,pet);
adoption3 = new Adoption(ZonedDateTime.now(),ZonedDateTime.now(),Status.CLOSED,user2,new Pet());
allAdoptions.add(adoption1);
allAdoptions.add(adoption2);
allAdoptions.add(adoption3);
petFilteredAdoptions.add(adoption2);
petFilteredAdoptions.add(adoption1);
userFilteredAdoptions.add(adoption2);
userFilteredAdoptions.add(adoption1);
Mockito.when(this.adoptionRepository.findById(0)).thenReturn(java.util.Optional.of(adoption1));
Mockito.when(this.adoptionRepository.findById(1)).thenReturn(java.util.Optional.of(adoption2));
Mockito.when(this.petRepository.findById(0)).thenReturn(java.util.Optional.of(pet));
Mockito.when(this.petRepository.findById(1)).thenReturn(java.util.Optional.of(petAlteadyAdopted));
Mockito.when(this.appUserRepository.findByUsername("username")).thenReturn(java.util.Optional.of(user));
Mockito.when(this.appUserRepository.findByUsername("username1")).thenReturn(java.util.Optional.of(user2));
Mockito.when(this.adoptionRepository.findAll()).thenReturn(allAdoptions);
Mockito.when(this.adoptionRepository.findAllByPet(pet)).thenReturn(petFilteredAdoptions);
Mockito.when(this.adoptionRepository.findAllByUser(user)).thenReturn(userFilteredAdoptions);
Mockito.when(this.adoptionRepository.findAllByUserAndPet(user,pet)).thenReturn(userFilteredAdoptions);
Mockito.when(this.adoptionRepository.save(Mockito.any(Adoption.class))).thenReturn(adoption1);
this.service = Mockito.spy(new AdoptionServiceImpl(this.adoptionRepository, this.petRepository,this.appUserRepository));
}
As a result, the following test fails, even though it should pass:
#Test
public void searchTest2() {
List<Adoption> adoptionList = this.service.search("",0);
Assert.assertEquals(petFilteredAdoptions,adoptionList);
}
In order to solve this I tried mocking the isPresent() method:
Mockito.when(this.petRepository.findById(0).isPresent()).thenReturn(true);
But I'm getting the following exception:
org.mockito.exceptions.misusing.WrongTypeOfReturnValue:
Boolean cannot be returned by findById() findById() should return
Optional***
If you're unsure why you're getting above error read on. Due to the
nature of the syntax above problem might occur because:
This exception might occur in wrongly written multi-threaded tests. Please refer to Mockito FAQ on limitations of concurrency
testing.
A spy is stubbed using when(spy.foo()).then() syntax. It is safer to stub spies -
with doReturn|Throw() family of methods. More in javadocs for Mockito.spy() method.
I also tried the following variation:
Mockito.doReturn(true).when(this.petRepository.findById(0)).isPresent();
But then I got the following exception:
org.mockito.exceptions.misusing.UnfinishedStubbingException:
Unfinished stubbing detected here:
-> at mk.finki.ukim.milenichinja.ServiceTests.AdoptionServiceFilterTests.init(AdoptionServiceFilterTests.java:87)
E.g. thenReturn() may be missing. Examples of correct stubbing:
when(mock.isOk()).thenReturn(true);
when(mock.isOk()).thenThrow(exception);
doThrow(exception).when(mock).someVoidMethod(); Hints:
missing thenReturn()
you are trying to stub a final method, which is not supported
you are stubbing the behaviour of another mock inside before 'thenReturn' instruction is completed
Any ideas how to solve this problem?
In the init method, you are stubbing findById on the mock instance this.petRepository to return a non-mock Optional, which is good. In your new test, you are trying to set a return value for isPresent, which you can't do because Optional is not a mock. If you want to override the behavior per-test, you'll need to stub findById to return an Optional of a different instance. Therefore, this is right, though it appears exactly as it does in init and consequently it can't tell you why your test is failing.
Mockito.when(this.petRepository.findById(0))
.thenReturn(java.util.Optional.of(pet));
Mockito works by creating a mock object that subclasses a class and overrides every method. The overridden method is what interacts with a static (ThreadLocal) infrastructure, allowing you to use when syntax. The important thing here is that when ignores its argument, and instead tries to mock the last interaction that you made with a mock. You can find out more in the SO questions How does mockito when() invocation work? and How do Mockito matchers work?.
When you see this call:
Mockito.when(this.petRepository.findById(0))
.thenReturn(java.util.Optional.of(pet));
Then it works as you've intended:
petRepository is a mock, findById is presumably an overridable method, Mockito records the fact that you've called it with the argument 0.
findById doesn't have any behavior stubbed yet, so it does its default, returning null.
when doesn't care that it just received null, because the null doesn't tell it anything about what methods were called to get the null. Instead it looks back at its most recent record (findById(0)) and returns an object with the thenVerb methods you expect.
You call thenReturn, so Mockito sets up petRepository to return the Optional instance you created and passed in.
But when you try this call:
Mockito.when(this.petRepository.findById(0).isPresent()).thenReturn(true);
Then the most recent interaction isn't isPresent, it's findById, so Mockito assumes you want findById(0) to thenReturn(true) and throws WrongTypeOfReturnValue. Optional is not a mock, so interacting with it doesn't let Mockito record its interaction or replay your behavior. For what it's worth, I also wouldn't advise mocking it: Optional is a final class, and though Mockito has recently added some support for mocking final types, Optional is simple and straightforward enough that it makes more sense to just return the Optional instance you want rather than trying to mock it.
With all that said, your code looks right; as long as PetRepository is an interface I don't see anything about the way your method looks or the way your mocks look that would cause this.petRepository.findById(0) to return an absent Optional. In fact, I don't even see where you would create an absent Optional for it to return, so I can only guess that you are using more real objects in your test than you think you are.

isPresent() method returns false (in testing with Mockito) [duplicate]

I am trying to test the following method with JUnit tests, using Mockito:
#Override public List<Adoption> search(String username, Integer id) {
List<Adoption> emptySearchResult = new ArrayList<>();
if(id != null && !username.equals("") ) {
if(!this.petRepository.findById(id).isPresent()){
return emptySearchResult;
}
if(!this.appUserRepository.findByUsername(username).isPresent()){
return emptySearchResult;
}
Pet pet = this.petRepository.findById(id).orElseThrow( () -> new PetNotFoundException(id));
AppUser user = this.appUserRepository.findByUsername(username).orElseThrow( () -> new UsernameNotFoundException(username));
return this.adoptionRepository.findAllByUserAndPet(user, pet);
}
else if(id != null && username.equals("")){
if(!this.petRepository.findById(id).isPresent()){
return emptySearchResult;
}
Pet pet = this.petRepository.findById(id).orElseThrow( () -> new PetNotFoundException(id));
return this.adoptionRepository.findAllByPet(pet);
}
else if(id == null && !username.equals("")) {
if(!this.appUserRepository.findByUsername(username).isPresent()){
return emptySearchResult;
}
AppUser user = this.appUserRepository.findByUsername(username).orElseThrow( () -> new UsernameNotFoundException(username));
return this.adoptionRepository.findAllByUser(user);
}
else {
return this.adoptionRepository.findAll();
}
}
However, I run into a problem with the following part:
if(!this.petRepository.findById(id).isPresent())
Even though I have mocked this.petRepository.findById(id), for some reason isPresent() is returning false. This is my initialization for the tests:
#Mock
private AdoptionRepository adoptionRepository;
#Mock
private PetRepository petRepository;
#Mock
private AppUserRepository appUserRepository;
private AdoptionServiceImpl service;
private Adoption adoption1;
private Adoption adoption2;
private Adoption adoption3;
private AppUser user;
private AppUser user2;
private Pet pet;
private Pet petAlteadyAdopted;
List<Adoption> allAdoptions = new ArrayList<>();
List<Adoption> userFilteredAdoptions = new ArrayList<>();
List<Adoption> petFilteredAdoptions = new ArrayList<>();
#Before
public void init() {
MockitoAnnotations.initMocks(this);
user = new AppUser("username","name","lastname","email#gmail.com","pass",ZonedDateTime.now(), Role.ROLE_USER, City.Skopje);
user2 = new AppUser("username1","name","lastname","email#gmail.com","pass",ZonedDateTime.now(), Role.ROLE_USER, City.Skopje);
Center center = new Center("a", City.Bitola,"url");
pet = new Pet("p", Type.DOG,"b", Gender.FEMALE,"d",center, ZonedDateTime.now(),"url",null,false,ZonedDateTime.now());
petAlteadyAdopted = new Pet("p", Type.DOG,"b", Gender.FEMALE,"d",center, ZonedDateTime.now(),"url",null,true,ZonedDateTime.now());
pet.setId(0);
petAlteadyAdopted.setId(1);
adoption1 = new Adoption(ZonedDateTime.now(),ZonedDateTime.now(),Status.ACTIVE,user,pet);
adoption2 = new Adoption(ZonedDateTime.now(),ZonedDateTime.now(),Status.CLOSED,user,pet);
adoption3 = new Adoption(ZonedDateTime.now(),ZonedDateTime.now(),Status.CLOSED,user2,new Pet());
allAdoptions.add(adoption1);
allAdoptions.add(adoption2);
allAdoptions.add(adoption3);
petFilteredAdoptions.add(adoption2);
petFilteredAdoptions.add(adoption1);
userFilteredAdoptions.add(adoption2);
userFilteredAdoptions.add(adoption1);
Mockito.when(this.adoptionRepository.findById(0)).thenReturn(java.util.Optional.of(adoption1));
Mockito.when(this.adoptionRepository.findById(1)).thenReturn(java.util.Optional.of(adoption2));
Mockito.when(this.petRepository.findById(0)).thenReturn(java.util.Optional.of(pet));
Mockito.when(this.petRepository.findById(1)).thenReturn(java.util.Optional.of(petAlteadyAdopted));
Mockito.when(this.appUserRepository.findByUsername("username")).thenReturn(java.util.Optional.of(user));
Mockito.when(this.appUserRepository.findByUsername("username1")).thenReturn(java.util.Optional.of(user2));
Mockito.when(this.adoptionRepository.findAll()).thenReturn(allAdoptions);
Mockito.when(this.adoptionRepository.findAllByPet(pet)).thenReturn(petFilteredAdoptions);
Mockito.when(this.adoptionRepository.findAllByUser(user)).thenReturn(userFilteredAdoptions);
Mockito.when(this.adoptionRepository.findAllByUserAndPet(user,pet)).thenReturn(userFilteredAdoptions);
Mockito.when(this.adoptionRepository.save(Mockito.any(Adoption.class))).thenReturn(adoption1);
this.service = Mockito.spy(new AdoptionServiceImpl(this.adoptionRepository, this.petRepository,this.appUserRepository));
}
As a result, the following test fails, even though it should pass:
#Test
public void searchTest2() {
List<Adoption> adoptionList = this.service.search("",0);
Assert.assertEquals(petFilteredAdoptions,adoptionList);
}
In order to solve this I tried mocking the isPresent() method:
Mockito.when(this.petRepository.findById(0).isPresent()).thenReturn(true);
But I'm getting the following exception:
org.mockito.exceptions.misusing.WrongTypeOfReturnValue:
Boolean cannot be returned by findById() findById() should return
Optional***
If you're unsure why you're getting above error read on. Due to the
nature of the syntax above problem might occur because:
This exception might occur in wrongly written multi-threaded tests. Please refer to Mockito FAQ on limitations of concurrency
testing.
A spy is stubbed using when(spy.foo()).then() syntax. It is safer to stub spies -
with doReturn|Throw() family of methods. More in javadocs for Mockito.spy() method.
I also tried the following variation:
Mockito.doReturn(true).when(this.petRepository.findById(0)).isPresent();
But then I got the following exception:
org.mockito.exceptions.misusing.UnfinishedStubbingException:
Unfinished stubbing detected here:
-> at mk.finki.ukim.milenichinja.ServiceTests.AdoptionServiceFilterTests.init(AdoptionServiceFilterTests.java:87)
E.g. thenReturn() may be missing. Examples of correct stubbing:
when(mock.isOk()).thenReturn(true);
when(mock.isOk()).thenThrow(exception);
doThrow(exception).when(mock).someVoidMethod(); Hints:
missing thenReturn()
you are trying to stub a final method, which is not supported
you are stubbing the behaviour of another mock inside before 'thenReturn' instruction is completed
Any ideas how to solve this problem?
In the init method, you are stubbing findById on the mock instance this.petRepository to return a non-mock Optional, which is good. In your new test, you are trying to set a return value for isPresent, which you can't do because Optional is not a mock. If you want to override the behavior per-test, you'll need to stub findById to return an Optional of a different instance. Therefore, this is right, though it appears exactly as it does in init and consequently it can't tell you why your test is failing.
Mockito.when(this.petRepository.findById(0))
.thenReturn(java.util.Optional.of(pet));
Mockito works by creating a mock object that subclasses a class and overrides every method. The overridden method is what interacts with a static (ThreadLocal) infrastructure, allowing you to use when syntax. The important thing here is that when ignores its argument, and instead tries to mock the last interaction that you made with a mock. You can find out more in the SO questions How does mockito when() invocation work? and How do Mockito matchers work?.
When you see this call:
Mockito.when(this.petRepository.findById(0))
.thenReturn(java.util.Optional.of(pet));
Then it works as you've intended:
petRepository is a mock, findById is presumably an overridable method, Mockito records the fact that you've called it with the argument 0.
findById doesn't have any behavior stubbed yet, so it does its default, returning null.
when doesn't care that it just received null, because the null doesn't tell it anything about what methods were called to get the null. Instead it looks back at its most recent record (findById(0)) and returns an object with the thenVerb methods you expect.
You call thenReturn, so Mockito sets up petRepository to return the Optional instance you created and passed in.
But when you try this call:
Mockito.when(this.petRepository.findById(0).isPresent()).thenReturn(true);
Then the most recent interaction isn't isPresent, it's findById, so Mockito assumes you want findById(0) to thenReturn(true) and throws WrongTypeOfReturnValue. Optional is not a mock, so interacting with it doesn't let Mockito record its interaction or replay your behavior. For what it's worth, I also wouldn't advise mocking it: Optional is a final class, and though Mockito has recently added some support for mocking final types, Optional is simple and straightforward enough that it makes more sense to just return the Optional instance you want rather than trying to mock it.
With all that said, your code looks right; as long as PetRepository is an interface I don't see anything about the way your method looks or the way your mocks look that would cause this.petRepository.findById(0) to return an absent Optional. In fact, I don't even see where you would create an absent Optional for it to return, so I can only guess that you are using more real objects in your test than you think you are.

Intercept method call on method that is tested OR get control over object created in method

I have currently this the issue that there is an object of Type CoolObj created within my method doSomething().
This object goes through some processes and at the end I want to verify if everything is ok and then continue.
Now the problem is that I cannot control the behavior and prevent an exception to be thrown.
Either hasErros() should return false or I kind of mute the verify method. But I could not figure out how.
Any idea how to solve this issue please?
public class ExampleClass {
public void doSomething(){
CoolObj coolObj = new CoolObj();
verify(coolObj);
}
private void verify(CoolObj coolObj) {
if(coolObj.hasErrors()){
throw new Exception(); //this is my issue
}
}
}
Basically, the problem that you have is related to the fact, that test doesn't control creation of CoolObj instances (that's because ExampleClass is not designed in testable way).
The best approach would be to change the design of ExampleClass, for example by delegating creating CoolObj instances to corresponding factory. Then mock instance created by factory and setup needed responses of hasErrors by Mockito.
However, if for some reason you are forced to leave the design of ExampleClass as is, then you can use PowerMock to mock construction of new instances.
For the code snippet provided in the question, following is an example of tests with and without exception thrown:
#RunWith(PowerMockRunner.class)
#PrepareForTest(ExampleClass.class)
public class ExampleClassTest {
private final ExampleClass exampleClass = new ExampleClass();
#Test
public void exceptionIsNotThrownIfCoolObjHasNoErrors() throws Exception {
CoolObj coolObjMock = Mockito.mock(CoolObj.class);
PowerMockito.whenNew(CoolObj.class).withNoArguments().thenReturn(coolObjMock);
Mockito.when(coolObjMock.hasErrors()).thenReturn(false);
Assertions.assertThatCode(exampleClass::doSomething)
.doesNotThrowAnyException();
}
#Test
public void exceptionIsThrownIfCoolObjHasErrors() throws Exception {
CoolObj coolObjMock = Mockito.mock(CoolObj.class);
PowerMockito.whenNew(CoolObj.class).withNoArguments().thenReturn(coolObjMock);
Mockito.when(coolObjMock.hasErrors()).thenReturn(true);
Assertions.assertThatThrownBy(exampleClass::doSomething)
.isInstanceOf(RuntimeException.class);
}
}
Notes:
For more details, regarding code snippet in the answer take a look here.
Please, note that to work properly PowerMock and Mockito should have compatible versions.

How to test a void method using JUnit using Mockito [duplicate]

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.
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());
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);
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());
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.
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.
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)
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());
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));
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.
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);
In your example you should mock Listener item and use Mockito.verify to check interactions with it

How do you expect and verify a private void method call using PowerMockito and TestNG?

I need to write unit tests against a pre-existing code base using TestNG, Mockito and now PowerMockito, to test private and static methods more easily. I am currently trying to write a test against a private void method in a class that we are testing, but am unable to figure it out. In the normal PowerMock API there are methods called replayAll(), verifyAll(), and expectLastCalled(), which are suitable for most purposes. I just can't find good docs that explain how to do it the PowerMockito way. Any explanations or insights on this would be much appreciated.
Method to test:
private void pVMethod(Type param) throws Exception {
param.setA(StaticClass.methodA().toString());
param.setB(StaticClass.methodB().toString());
// getMemo(String label) is in a public class in same package
param.setC(getMemo("memo.provider"));
param.setD(getMemo("memo.item"));
try {
param.setTimestamp(DataTypeFactory.newInstance().newXMLGregorianCalendar(newjava.util.GregorianCalendar()));
} catch (SomeException e) {
...
throw new Exception();
}
}
test attempt:
#Test(expectedExceptions = Exception.class)
public void pVMethod() throws Exception {
TestClass testMock = mock(TestClass.class);
Exception exception = mock(Exception.class);
// StaticClass staticClassMock = mock(StaticClass.class); ??
mockStatic(StaticClass.class);
// when(..) and thenReturn(..) are static imports from PowerMockito library
when(StaticClass.methodA()).thenReturn("stubStringA");
when(StaticClass.methodB()).thenReturn("stubStringB");
doThrow(exception).when(param).setTimestamp(Mockito.any(XMLGregorianCalendar.class));
// Docs say method name can be inferred via reflection
Whitebox.invokeMethod(tested, event);
// this is where things are hairy. testedSpy is defined at the top level
verifyPrivate(testedSpy).pVMethod(testMock);
}
Ok, here is the answer:
In PowerMockito, if you want to verify the behavior of a private void method you use the verifyPrivate() method, but you have to do it like this:
verifyPrivate(tested).invoke("privateMethodName", argument);
Notice the use of the invoke method, missing from the last line of the OP.
NOTE: You do not have to use a doNothing().when(mock.privateMethod()) statement, because void methods in mock objects don't do anything by default.
example taken from here

Categories