How to mock one method with different parameters multiple times? - java

I have some testing method:
#Test
public void test_method() {
MyObj mock = mock(MyObj.class);
when(mock.get("testName", "1")).thenReturn("Result1");
when(mock.get("test", "2")).thenReturn("rrrr");
}
when I trying run this method I had exception:
org.mockito.exceptions.misusing.PotentialStubbingProblem:
Strict stubbing argument mismatch. Please check:
Typically, stubbing argument mismatch indicates user mistake when writing tests.
Mockito fails early so that you can debug potential problem easily.
However, there are legit scenarios when this exception generates false negative signal:
- stubbing the same method multiple times using 'given().will()' or 'when().then()' API
Please use 'will().given()' or 'doReturn().when()' API for stubbing.
- stubbed method is intentionally invoked with different arguments by code under test
Please use default or 'silent' JUnit Rule (equivalent of Strictness.LENIENT).
For more information see javadoc for PotentialStubbingProblem class.
How can I mock this method?

The error message tells you:
stubbing the same method multiple times using 'given().will()' or 'when().then()' API
Please use 'will().given()' or 'doReturn().when()' API for stubbing.

You can use any() to mock one method with different parameters. any() will be used as a dynamic variable.
e.g doReturn(order).when(orderRepository).save(any(Order.class));
I was saving orderRepository with 2 different Order parameters so I have used any with Order class.

Related

Does when() in Mockito verify invocation with strict parameter?

I am curious if when(mock.method(SPECIFIC_PARAM)).thenReturn(RETURN_VAlUE); checks for invocation of method. Or should I do verify(mock).method(SPECIFIC_PARAM); to make sure the method has been invoked?
If you use #RunWith(MockitoJUnitRunner.class) or #ExtendWith(MockitoExtension.class), you will get an UnnecessaryStubbingException after running a test if you provided a mock that is not used.
You can avoid this by setting #MockitoSettings(strictness = Strictness.LENIENT) if you want to.
But even with the UnnecessaryStubbingException, it's still better to do an actual verify inside your test for readability. Your 'verify' can also be stricter than your stub, e.g:
when(mock.method(anyCollection())).thenReturn(...)
...
mock.method(collection);
...
verify(mock).method(argThat(collection -> collection.size()==1));
Basically when(mock.method(SPECIFIC_PARAM)).thenReturn(RETURN_VAlUE); signifies that when the method is invoked on the mock then the specified value is returned. The whenis used to configure the mocking, so it doesn't perform any verifications. It is the verify that checks if a method is called.
With the method when(..).thenReturn(...) you are defining a rule on the mock object, in your particular case, you are instructing the mock object to return "RETURN_VALUE" when the method mock.method is invoked with input parameter "SPECIFIC_PARAM".
You are actually not asserting anything over the mock object.
Instead, you can check the effective invocation of the mock's method, by using verify(mock), as you did in your description.
For example, this is what you should expect when your method is not invoked:
got: <Wanted but not invoked: mock.method(SPECIFIC_PARAM);

How do I get Mockito mocks to cause a failure when unexpected calls are made?

I have some mock objects that are probably going to get passed around a bit and might end up being fairly complex.
I'd like to either have Mockito output a log for each call made to a mock or I'd like it to fail whenever an unexpected call is made so I can iterate through those calls and set up appropriate responses.
How can I accomplish this?
The most-idiomatic way of doing this is with verifyNoMoreInteractions, as in Mockito docs #8:
//interactions
mock.doSomething();
mock.doSomethingUnexpected();
//verification
verify(mock).doSomething();
//following will fail because 'doSomethingUnexpected()' is unexpected
verifyNoMoreInteractions(mock);
I say "most-idiomatic" above because that method has its own warning label, which links to the blog post "Should I worry about the unexpected?" by Mockito originator Szczepan Faber.
verifyNoMoreInteractions() is not recommended to use in every test method. verifyNoMoreInteractions() is a handy assertion from the interaction testing toolkit. Use it only when it's relevant. Abusing it leads to overspecified, less maintainable tests.
In short, you should have a very clear reason to check what your dependency is not doing or what your system-under-test is not calling, as opposed to what they are doing and calling. You might use verifyNoMoreInteractions for an RPC object, if you want to avoid unnecessary RPC calls, but not (say) a calculator with no side effects. Even better is to specify your exact requirements with never() or times(int) as parameters to verify.
That said, there are two even-less-idiomatic ways of doing this:
You can take an overall log of calls made using mockingDetails(Object) and iterating through getInvocations(). That should reflectively give you a whole list of the invocations. I have a hard time imagining how this would be useful in a test, but it might be useful in cleaning up a nebulous or poorly-documented existing system.
You can make the mock's default action to throw an exception, which means that if anyone calls something that you haven't stubbed, the test will immediately fail.
// untested pseudocode
YourObject yourObject = Mockito.mock(YourObject.class, withSettings()
.defaultAnswer(invocation -> {
throw new UnsupportedOperationException(invocation.toString());
}));
Sure, that'd work, but you'd not only be violating one of Mockito's core principles (mocks are nice by default, using EasyMock's definition of "nice"), but you'd also force yourself to only stub using doVerb (doReturn, doAnswer, etc) because calls to when(yourObject.doAnything()) would necessarily throw that exception before the call to when would even run.
Developers who are familiar with Mockito would likely say that this exception-prone cure is worse than the disease, and may be useful only for temporarily diagnosing the most tangled legacy code.
I was just asking myself the same question and... The solution using ReturnsSmartNulls will return SmartNulls instead of null... So it is meaningful for non-void methods only right ? What about void methods, the ones having side effects ?
In my opinion, if you want to make sure that your test fails when a method of your mock is called without your explicit behavior definition of it (doXXX(...).when(...) mockito methods) you can initialize your mocks with a custom default answer that will throw an exception, or better... fail your test.
For example you can add the following class inside your test class (or outside if you intend to use it elsewhere, or even use a MockitoConfiguration class as previously mentionned depending on what you want):
static class FailAnswer implements Answer<Object> {
#Override
public Object answer(InvocationOnMock invocation) {
String methodName = invocation.getMethod().getName();
String className = invocation.getMethod().getDeclaringClass().getSimpleName();
return fail(String.format("%s#%s should not have been called", className, methodName));
}
}
Then init your mock with this fake answer in your setUp method :
#BeforeEach
void setUp() {
delegateService = mock(DelegateService.class, new FailAnswer());
classUnderTest = new ClassUnderTest(delegateService);
}
Unfortunately, this solution is not compatible with #Mock annotation which only takes native predefined answers from org.mockito.Answers enum as argument. So that forces you to manually init every mock, spy, captor within the setUp method (RIP MockitoAnnotations.initMocks(this))
Benefit :
you get rid of default behavior of mockito mocks, sometimes hidding unintentionnal uses of mocks for specifics use cases (does is really matter ?)
=> You must define everything you use (inside tests or tests fixtures)
=> you don't have to make verification to make sure your test have not invoked methods it shouldn't have.
Drawbacks :
This is an unusual usage of mockito, so this makes your test less affordable
You give up on MockitoAnnotations feature
As you override mockito default stubbing, you must use the stubbing form do().when() instead of when(...).do(....), the latter providing au type-checking unlike the former.
Warning : This solution doesn't garantee your mock is called, it just garantees that the method you don't stub won't be called. It doesn't come as replacement for counting methods invocations neither.
The best answer I found is to configure Mockito to return SmartNulls.
https://static.javadoc.io/org.mockito/mockito-core/2.6.9/org/mockito/Mockito.html#RETURNS_SMART_NULLS
This implementation can be helpful when working with legacy code. Unstubbed methods often return null. If your code uses the object returned by an unstubbed call you get a NullPointerException. This implementation of Answer returns SmartNull instead of null. SmartNull gives nicer exception message than NPE because it points out the line where unstubbed method was called. You just click on the stack trace.
You can do it by mock or by default (might cause problems with other frameworks like Spring).
Manually
Writer writerMock = mock(Writer.class, RETURNS_SMART_NULLS);
Annotation
#Mock(answer = Answers.RETURNS_SMART_NULLS)
Set as Global Default
Configuration class must be in exactly this package. This might lead to strange failures with Spring.
package org.mockito.configuration;
import org.mockito.internal.stubbing.defaultanswers.ReturnsSmartNulls;
import org.mockito.stubbing.Answer;
public class MockitoConfiguration extends DefaultMockitoConfiguration {
public Answer<Object> getDefaultAnswer() {
return new ReturnsSmartNulls();
}
}
See: https://solidsoft.wordpress.com/2012/07/02/beyond-the-mockito-refcard-part-1-a-better-error-message-on-npe-with-globally-configured-smartnull/
I had problems with SpringBootRepositories and #MockBean when enabling the global default:
java.lang.ClassCastException: org.mockito.codegen.Object$MockitoMock$191495750 cannot be cast to xxx.xxx.MyObject
Example of error output
org.junit.ComparisonFailure: expected:<[DataRecordType{id=null, name='SomeRecord', pathTemplate='SomeTemplate'}]> but was:<[SmartNull returned by this unstubbed method call on a mock: dataRecordTypeRepository bean.getById(1L);]>
If you are trying to track the flow, you can use Mockito verify to check if certain call has been made.
verify(yourMockedObject).yourMethod();
you can also use times to verify if certain call has to be made exactly some number of times.
verify(yourMockedObject, times(4)).yourMethod();
It is not a good practice to make your unit test complex. Try to test only small unit of your code at a time.

mockito : mock method call with parameters by reflection

I'm using mockito and developping with java6 and spring.
I'm working on a test API for some developpers and I propose a few methods for mocking objects and methods (it's a legacy code...).
Now, I want to replace all this things by mockito but I always propose a test API. So, I developped some methods using mockito.
I have an old method with two parameters (String). A first parameter is a mocked service id and its method with parameters. And the second parameter is the returned Object.
Example :
mockReturnObject("myServiceId.myMethod(String, Integer)", myReturnedObject);
Now, I want to use mock, when and thenReturn mockito methods, and I don't see how...
Perhaps with reflection but with "when" method it's impossible because mockito need the effective method.
How can I do that ? thanks.
This is a bad idea: you're trying to reimplement some of the systems Mockito already provides while losing out on many of the features Mockito offers. However, there is a way to make this work, with some difficulty. The key is to write a custom Answer, make it the default answer for the mock, and then compare your object, method name, and method parameter types using InvocationOnMock.
public class ReflectiveMockAnswer implements Answer<Object> {
#Override public Object answer(InvocationOnMock invocation) {
// Assume you've successfully parsed each String into a StubbedResponse, with
// Object target, String method, String[] argTypes, and Object returnValue.
// A Set would beat a for-loop here, should you need to optimize.
for (StubbedResponse stubbedResponse : allStubbedResponses) {
if (stubbedResponse.target == invocation.getMock()
&& stubbedResponse.method.equals(invocation.getMethod().getName())
&& stringArraysEqual(stubbedResponse.argTypes,
typeNamesFrom(invocation.getMethod().getParameterTypes())) {
return stubbedResponse.returnValue;
}
}
throw new RuntimeException("Unstubbed method called.");
}
}
// Later...
Object yourMockObject = Mockito.mock(classToMock, new ReflectiveMockAnswer());
At that point, you've implemented a simplified version of Mockito within and based on the full version of Mockito. You'll also need to:
Parse the string into a StubbedResponse, probably with regular expressions
Identify the field in your bean-under-test by name
Replace that field with a mock of the appropriate class, created as above, before the bean-under-test has a chance to interact with it
...and acknowledge that this solution doesn't handle:
Verification
Any sort of argument matching, including basic "equals" matching
Name collisions in parameter types (com.foo.SomeClass vs com.bar.SomeClass)
Repeated calls (thenReturn(1, 2, 3).thenThrow(new RuntimeException()))
...and cannot handle:
Code search tools: you can only tell which methods are mocked other than by searching for strings, not with tools like "Find references" in Eclipse the way Mockito can
Compile-time checking and automated refactoring tools: your tests would break at runtime if field names, method names, or parameters change; Mockito doesn't have that problem
Final methods: Mockito can't, so you can't either
Unless this is a "straw man" or very temporary solution, I recommend strongly to just introduce Mockito directly into your test cases, one test at a time.

How to mock method with any arguments?

I write following row in my test:
when(Product.fromNode(any(Node.class), any(Locale.class),anyString())).thenReturn(productMock);
I see following error message:
you cannot use argument matchers outside of verification or stubbing
It is impposible because I can't use any at this case. But I don't know what concrete argument will be passed to the method but I know that I should handle it same.
What can you advice for me ?
Problem was that I used PowerMockito.mockStatic
but I used RunWith annotation different than:
#RunWith(PowerMockRunner.class)

java.lang.IllegalStateException: incompatible return value type

I am using EasyMock to create mock classes in test cases.
expect(entity.getType()).andReturn("string");
Type belongs to the String datatype. In my development environment it is working fine. But If I transfer to my server and take build, it is failing with following error:
java.lang.IllegalStateException: incompatible return value type
I don't know why it is failing in server and getting executed in my development machine.
Development EasyMock version: 2.5.2
I just had the same problem.
I had a partial mock in EasyMock, but forgot to call addMockedMethod for the method I wanted to set the expectation for.
The error message was the same as above, I'd say that was somewhat misleading.
In my case the method was final on which I called expect on. So EasyMock was not able to mock the method and thus did not record the method invocation.
Make sure that your entity object is not a simple POJO (eg. new Entity()), and it was created with the createMock() methods of EasyMock.
For me, I had to remember to call reset(...) on it after having used it once, or I (bizarrely) got this error message calling expect on it a second time.
If anyone gets this error when trying to mock behavior of a class's toString() method, e.g. expect(entity.toString()).andReturn("string");, EasyMock will instead invoke a special version of the toString() method on that line in the test, and will not modify the expected behavior of the method in the code under test. You basically can't mock any toString() behavior, and your test will liekly still pass if you just remove the line where you're trying to mock the call. PowerMock does not seem to offer any solutions in this case, since its #PrepareForTest doesn't change this behavior.

Categories

Resources