Mockito match specific Class argument - java

I am trying to mock some resources that are generated dynamically. In order to generate these resources, we must pass in a class argument. So for example:
FirstResourceClass firstResource = ResourceFactory.create(FirstResourceClass.class);
SecondResourceClass secondResource = ResourceFactory.create(SecondResource.class);
This is well and good until I tried to mock. I am doing something like this:
PowerMockito.mockStatic(ResourceFactory.class);
FirstResourceClass mockFirstResource = Mockito.mock(FirstResourceClass.class);
SecondResourceClass mockSecondResource = Mockito.mock(SecondResourceClass.class);
PowerMockito.when(ResourceFactory.create(Matchers.<Class<FirstResourceClass>>any()).thenReturn(mockFirstResource);
PowerMockito.when(ResourceFactory.create(Matchers.<Class<SecondResourceClass>>any()).thenReturn(mockSecondResource);
It seems like the mock is being injected into the calling class, but FirstResourceClass is being send mockSecondResource, which throws a compile error.
The issue is (I think) with the use of any() (which I got from this question). I believe I have to use isA(), but I'm not sure how to make that method call, as it requires a Class argument. I have tried FirstResourceClass.class, and that gives a compile error.

You want eq, as in:
PowerMockito.when(ResourceFactory.create(Matchers.eq(FirstResourceClass.class)))
.thenReturn(mockFirstResource);
any() ignores the argument, and isA will check that your argument is of a certain class—but not that it equals a class, just that it is an instanceof a certain class. (any(Class) has any() semantics in Mockito 1.x and isA semantics in 2.x.)
isA(Class.class) is less specific than you need to differentiate your calls, so eq it is. Class objects have well-defined equality, anyway, so this is easy and natural for your use-case.
Because eq is the default if you don't use matchers, this also works:
PowerMockito.when(ResourceFactory.create(FirstResourceClass.class))
.thenReturn(mockFirstResource);
Note that newer versions of Mockito have deprecated the Matchers name in favor of ArgumentMatchers, and Mockito.eq also works (albeit clumsily, because they're "inherited" static methods).

Related

Equivalent of Answers.RETURNS_DEEP_STUBS for a spy in mockito

I have been unable to find a way to use "Deep Stubs" for stubbing methods on a spy in Mockito. What I'm looking to do is something like this:
#Spy private Person person = //retrieve person
#Test
public void testStubbed() {
doReturn("Neil").when(person).getName().getFirstName();
assertEquals("Neil", person.getName().getFirstName());
}
The code above compiles with no issues, but upon running the test, it fails saying that the return type (the Name class, in this case) cannot be returned by getName().
Normally, when mocking, you have to use
#Mock(answer = Answers.RETURNS_DEEP_STUBS) for each mocked object. However, spy does not seem to have anything like this.
Has anyone ever successfully done deep stubbed mocking using a spy?
The error I'm receiving is listed below:
String cannot be returned by getName()
getName() should return Name
Due to the nature of the syntax above problem might occur because of:
1. Multithreaded testing
//I'm not doing multithreaded testing
2. A spy is stubbed using when(spy.foo()).then() syntax. It is safer to stub spies with doReturn|Throw() family of methods
//As shown above, I'm already using the doReturn family of methods.
While I'd still like to know if there's a better way to do this, I'd like to post a solution for anyone who comes looking.
The solution below works fine, by requiring you to create a new mock (or even a real object/spy) for each level of your dependencies. In other words, instead of chaining your method calls to create your stub, you mock each level individually.
#Spy private Person person = //retrieve person
#Mock private Name name;
#Test
public void testStubbed() {
doReturn(name).when(person).getName();
doReturn("Neil").when(name).getName();
assertEquals("Neil", person.getName().getFirstName());
}
You can get a little closer to the deep stubs you want by using doAnswer(RETURNS_DEEP_STUBS), but you can't override arbitrarily-deep method calls without taking care to stub their parent calls. I'd stick to manual single-level-deep mocks as you do in your answer, or use even less mocking if possible.
A spy's default behavior is to delegate to its real method call, which will typically return a real object (like your Name) and not a Mockito spy. This means that you won't normally be able to change those objects' behavior using Mockito: a spy isn't really the same class as the object being spied on, but rather is a generated subclass where every field value is copied from the spied-on value. (The copying is an important feature, because a delegating spy would have very unintutitive behavior regarding this, including for method calls and field values.)
Foo foo = new Foo();
foo.intValue = 42;
foo.someObject= new SomeObject();
Foo fooSpy = Mockito.spy(foo);
// Now fooSpy.intValue is 42, fooSpy.someObject refers to the exact same
// SomeObject instance, and all of fooSpy's non-final methods are overridden to
// delegate to Mockito's behavior. Importantly, SomeObject is not a spy, and
// Mockito cannot override its behavior!
So this won't work:
doReturn("Neil").when(person).getName().getFirstName();
// Mockito thinks this call ^^^^^^^^^ should return "Neil".
And neither will this:
doReturn("Neil").when(person.getName()).getFirstName();
// The object here ^^^^^^^^^^^^^^^^ won't be a mock, and even if Mockito
// could automatically make it a mock, it's not clear whether that
// should be the same spy instance every time or a new one every time.
In your situation, I'd choose the following, in order from most preferable to least:
Create a real Name object and install it using doReturn. It looks like Name is a data object (aka value object) after all, which likely means it has no dependencies, solid behavior, and difficult-to-mock state transitions. You may not be gaining anything by mocking it.
Create a mock Name and install it as you do in your answer. This is particularly useful if Name is more complicated than it looks to be, or if it doesn't actually exist in the first place.
Replace getName to return a deep stub...
doAnswer(RETURNS_DEEP_STUBS).when(person).getName();
...which you can then override...
doReturn("Neil").when(person.getName()).getFirstName();
...even for arbitrarily deep values.
doReturn("Gaelic").when(person.getName()
.getEtymology()
.getFirstNameEtymology())
.getOrigin();
As a final editorial, one of the hazards of partial mocks is that it makes it really hard to tell which behavior is real and which is faked; this might make it hard for you to guarantee that the behavior you're testing is prod behavior and not mock behavior. Another hazard of deep stubbing is that you may be violating the Law of Demeter by definition. If you find yourself using this kind of technique often in tests, it may be time to consider rearchitecting your system under test.
#Test
public void listTypeTest() throws Exception {
doCallRealMethod().when(listType).setRecordCount(new BigInteger("556756756756"));
listType.setRecordCount(new BigInteger("556756756756"));
doCallRealMethod().when(listType).getRecordCount();
assertEquals(new BigInteger("556756756756"), listType.getRecordCount());
}

Mockito.any returns null

I am trying to mock a static method with parameters like this :
Mockito.when(StaticClass.staticMethod(Mockito.any(A.class),
Mockito.any(B.class), SomeEnum.FOO))
.thenReturn(true);
I have added the following annotations :
#RunWith(PowerMockRunner.class)
#PowerMockRunnerDelegate(Parameterized.class)
#PrepareForTest({StaticClass.class, A.class, B.class})
But Mockito.any always returns null. Why ?
Short answer: Use doReturn().when() instead of when().then()
Long answer can be found over here:
How do Mockito matchers work?
Matchers return dummy values such as zero, empty collections, or null.
Mockito tries to return a safe, appropriate dummy value, like 0 for
anyInt() or any(Integer.class) or an empty List for
anyListOf(String.class). Because of type erasure, though, Mockito
lacks type information to return any value but null for any()
NullPointerException or other exceptions: Calls to
when(foo.bar(any())).thenReturn(baz) will actually call foo.bar(null),
which you might have stubbed to throw an exception when receiving a
null argument. Switching to doReturn(baz).when(foo).bar(any()) skips
the stubbed behavior.
Side Note: This issue could also be described something like, How to use Mockito matchers on methods that have precondition checks for null parameters?
Firstly, you can't mix matchers with actual arguments. You should use a matcher for the SomeEnum argument too:
Mockito.when(StaticClass.staticMethod(Mockito.any(A.class),
Mockito.any(B.class), Mockito.eq(SomeEnum.FOO))
.thenReturn(true);
Secondly, the any() methods should return null. That is exactly what they do. If you look at the code for these methods they return the default value for the class type if it is primitive wrapper object (like Integer, Boolean etc.) otherwise it returns null:
public <T> T returnFor(Class<T> clazz) {
return Primitives.isPrimitiveOrWrapper(clazz) ? Primitives.defaultValueForPrimitiveOrWrapper(clazz) : null;
}
You are getting things wrong. The one and only purpose of matcher methods such as any() is to match the arguments that come in at execution time.
You use these methods to instruct the mocking framework what calls you expect to happen. Or the other way round you use them to say: if this or that is coming in as argument then do this or that.
Therefore you absolutely do not care about the results of matcher invocations.
In that sense your question is indicating that your usage of the mocking framework is going the wrong way. Thus the only answer we can give regarding your current input: A) do some more research how to use mocking and B) then rework your question to be clear about your problem.
It was because it was a Parameterized test, and I did the mockStatic in the #Before method. When I do the mockStatic in the same method, it works.

Mockito verifies wrong overloaded method

I'm trying to write a unit test for a class that uses as a dependency an AsynchronousSocketChannel:
final AsynchronousSocketChannel channel = mock(AsynchronousSocketChannel.class);
final Client client = new Client(channel);
client.read();
verify(channel).read(isA(ByteBuffer.class), eq(client), isA(CompletionHandler.class));
However, I'm receiving the following error:
Invalid use of argument matchers!
5 matchers expected, 3 recorded:
This happens because AsynchronousSocketChannel.read has 4 different overloaded versions, and for some reason verify keeps choosing the one with 5 arguments, even if the matchers I'm passing match the version with read(ByteBuffer dst, A attachment, CompletionHandler<Integer,? super A> handler).
In this answer it's suggested that this could really be a problem with the actual compiler, and that it's possible to instruct the compiler to choose the right overloaded method with something like
verify(channel).read(
ArgumentMatchers.<ByteBuffer>isA(ByteBuffer.class),
ArgumentMatchers.<Client>eq(client),
ArgumentMatchers.<CompletionHandler>isA(CompletionHandler.class)
);
but doing this I keep getting the same error.
Any idea if it's possible to make this work? Otherwise I believe I could just use the 5 params overloading, passing null as the additional 2 params, but it would be a bit like a hack to me.
You're trying to set expectations on a final method.
public final <A> void read(ByteBuffer dst,
A attachment,
CompletionHandler<Integer,? super A> handler)
Mockito can't override that method in the mock, so it's actually calling the real method. And that method is calling through to the overload with 5 parameters, and it's in that method where it is interacting with the mockito framework.
If you can construct the Client with an AsynchronousByteChannel instead of a AsynchronousSocketChannel, you could use that instead. This would work as you expect, because the 3-parameter overload isn't final on that class.
Otherwise, all you can do (with Mockito) is to set the expectations of what that 5-parameter overload will be called with.

mockito when with third party class

I need to use a third party class in Mockito.when as parameter. The class does not have equals implementation and hence Mockito.when always returns null except the case where any() is used.
The below always returns null:
when(obj.process(new ThirdParytClass())).thenReturn(someObj);
however, this works
when(obj.process(any(ThirdParytClass.class))).thenReturn(someObj);
But, the problem is the process() method is called twice in the actual code and the use of any() is ambiguous and does not help in covering the multiple scenarios to test.
Extending the class does not help and also leads to other complications.
Is there a way to address the issue.
If a class doesn't implement a (sensible) equals(Object), you can always match instances yourself by implementing your own ArgumentMatcher. Java 8's functional interfaces make this pretty easy to write (not that it was such a big hardship in earlier versions, but still):
when(obj.process(argThat(tpc -> someLogic()))).thenReturn(someObj);
More often than not, however, if you just want to compare the class' data members, the built-in refEq matcher would do the trick:
ThirdParytClass expected = new ThirdParytClass();
// set the expected properties of expected
when(obj.process(refEq(expected))).thenReturn(someObj);
Mockito provides the captor feature that may help you to bypass limitations of equals() method because overriding equals() to make a test pass may be desirable but it is not always the case.
And besides, sometimes, equals() may not be overridable. It is your use case.
Here is a example code with an ArgumentCaptor :
#Mock
MyMockedClass myMock;
#Captor
ArgumentCaptor argCaptor;
#Test
public void yourTest() {
ThirdPartyClass myArgToPass = new ThirdPartyClass();
// call the object under test
...
//
Mockito.verify(myMock).process(argCaptor.capture());
// assert the value of the captor argument is the expected onoe
assertEquals(myArgToPass , argCaptor.getValue());
}

What's the difference between Mockito Matchers isA, any, eq, and same?

I am confused on what's the difference between them, and which one to choose in which case. Some difference might be obvious, like any and eq, but I'm including them all just to be sure.
I wonder about their differences because I came across this problem:
I have this POST method in a Controller class
public Response doSomething(#ResponseBody Request request) {
return someService.doSomething(request);
}
And would like to perform a unit test on that controller.
I have two versions. The first one is the simple one, like this
#Test
public void testDoSomething() {
//initialize ObjectMapper mapper
//initialize Request req and Response res
when(someServiceMock.doSomething(req)).thenReturn(res);
Response actualRes = someController.doSomething(req);
assertThat(actualRes, is(res));
}
But I wanted to use a MockMvc approach, like this one
#Test
public void testDoSomething() {
//initialize ObjectMapper mapper
//initialize Request req and Response res
when(someServiceMock.doSomething(any(Request.class))).thenReturn(res);
mockMvc.perform(post("/do/something")
.contentType(MediaType.APPLICATION_JSON)
.content(mapper.writeValueAsString(req))
)
.andExpect(status().isOk())
.andExpect(jsonPath("$message", is("done")));
}
Both work well. But I wanted my someServiceMock.doSomething() in the MockMvc approach to receive req, or at least an object that has the same variable values as req (not just any Request class), and return res, just like the first. I know that it's impossible using the MockMvc approach (or is it?), because the object passed in the actual call is always different from the object passed in the mock. Is there anyway I can achieve that? Or does it even make sense to do that? Or should I be satisfied using any(Request.class)? I've tried eq, same, but all of them fail.
any() checks absolutely nothing. Since Mockito 2.0, any(T.class) shares isA semantics to mean "any T" or properly "any instance of type T".
This is a change compared to Mockito 1.x, where any(T.class) checked absolutely nothing but saved a cast prior to Java 8: "Any kind object, not necessary of the given class. The class argument is provided only to avoid casting."
isA(T.class) checks that the argument instanceof T, implying it is non-null.
same(obj) checks that the argument refers to the same instance as obj, such that arg == obj is true.
eq(obj) checks that the argument equals obj according to its equals method. This is also the behavior if you pass in real values without using matchers.
Note that unless equals is overridden, you'll see the default Object.equals implementation, which would have the same behavior as same(obj).
If you need more exact customization, you can use an adapter for your own predicate:
For Mockito 1.x, use argThat with a custom Hamcrest Matcher<T> that selects exactly the objects you need.
For Mockito 2.0 and beyond, use Matchers.argThat with a custom org.mockito.ArgumentMatcher<T>, or MockitoHamcrest.argThat with a custom Hamcrest Matcher<T>.
You may also use refEq, which uses reflection to confirm object equality; Hamcrest has a similar implementation with SamePropertyValuesAs for public bean-style properties. Note that on GitHub issue #1800 proposes deprecating and removing refEq, and as in that issue you might prefer eq to better give your classes better encapsulation over their sense of equality.
If your Request.class implements equals, then you can use eq():
Bar bar = getBar();
when(fooService.fooFxn(eq(bar)).then...
The above when would activate on
fooService.fooFxn(otherBar);
if
otherBar.equals(bar);
Alternatively, if you want to the mock to work for some other subset of input (for instance, all Bars with Bar.getBarLength()>10), you could create a Matcher. I don't see this pattern too often, so usually I create the Matcher as a private class:
private static class BarMatcher extends BaseMatcher<Bar>{
...//constructors, descriptions, etc.
public boolean matches(Object otherBar){
//Checks, casts, etc.
return otherBar.getBarLength()>10;
}
}
You would then use this matcher as follows:
when(fooService.fooFxn(argThat(new BarMatcher())).then...
Hope that helps!

Categories