Mockito: Stub method with complex object as a parameter - java

Maybe this is a newbie question, but can't find the answer.
I need to stub a method with Mockito. If the method has "simple" arguments, then I can do it. For example, a find method with two parameters, car color and number of doors:
when(carFinderMock.find(eq(Color.RED),anyInt())).thenReturn(Car1);
when(carFinderMock.find(eq(Color.BLUE),anyInt())).thenReturn(Car2);
when(carFinderMock.find(eq(Color.GREEN), eq(5))).thenReturn(Car3);
The problem is that the find argument is a complex object.
mappingFilter = new MappingFilter();
mappingFilter.setColor(eq(Color.RED));
mappingFilter.setDoorNumber(anyInt());
when(carFinderMock.find(mappingFilter)).thenReturn(Car1);
This code does not work. The error is "Invalid use of argument matchers! 1 matchers expected, 2 recorded".
Can't modify the "find" method, it needs to be a MappingFilter parameter.
I suppose that I have to do "something" to indicate Mockito that when the mappingFilter.getColor is RED, and mappingFilter.getDoorNumber is any, then it has to return Car1 (and the same for the other two sentences).
But how?

Use a Hamcrest matcher, as shown in the documentation:
when(carFinderMock.find(argThat(isRed()))).thenReturn(car1);
where isRed() is defined as
private Matcher<MappingFilter> isRed() {
return new BaseMatcher<MappingFilter>() {
// TODO implement abstract methods. matches() should check that the filter is RED.
}
}

Since 2.1.0 Mockito has its own matcher mechanism build on top of org.mockito.ArgumentMatcher interface. This allows to avoid using Hamcrest. Usage is almost of same as with Hamcrest. Keep in mind that ArgumentMatcher is a functional interface and implementation of a matched can be expressed as a lambda expression.
private ArgumentMatcher<SomeObject> isYellow() {
return argument -> argument.isYellow();
}
and then
when(mock.someMethod(argThat(isYellow()).thenReturn("Hurray");

You need to correctly implement equals() method of your MappingFilter. In equals() you should only compare color and not doorNumber .
In simplest form, it should look like this -
#Override
public boolean equals(Object obj) {
MappingFilter other = (MappingFilter) obj;
return other.getColor() == this.getColor();
}
Also, you should form your MappingFilter simply as below instead of using any matcher such as eq
mappingFilter = new MappingFilter();
mappingFilter.setColor(Color.RED);
mappingFilter.setDoorNumber(10); //Any integer

Related

How can I unit test a method with multiple internal calls to class I want to mock using EasyMock

I would like to unit test a method with multiple internal calls to a class I want to mock using EasyMock.
The test method actually runs 5 times and calls the mocked method.
During each loop, I will create some objects, all of the same class (let's say of class A).
The private method will call the mock object method that takes the instance of class A, evaluate it and return a result.
In the end, the public method will return a List of results.
I tried the standard EasyMock.expect(MockClass.method(A)).andReturn() but it does not work since there is no implementation of equals() for class A:
// this is the method example I am trying to test
public methodToTest(){
// some logic
privateMethodToTest(x);
// some logic
}
private List<B> privateMethodToTest(int x){
List<B> list = new ArrayList<>();
List<A> all = getObjects(x); //getObjects private method
for (A a:all){
list.add(objectToMock.methodToMock(a));
return list;
}
This is how I would like it to work:
EasyMock.createMock(ObjectToMock.class);
EasyMock.expect(ObjectToMock.methodToMock(A)/* when A.getValue() == 1 */.andReturn("B object number 1")
EasyMock.expect(ObjectToMock.methodToMock(A)/* when A.getValue() == 2 */.andReturn("B object number 2")
//... and so on
//object of class A does not implement equals()
I am not sure how to do it and I was not able to find any similar example or answer to my question.
You need another matcher. By default, EasyMock will indeed match using equals. But you can't do that. Your basic choices are:
You don't care about matching precisely
If seems to be the easiest for you. It means doing:
expect(objectToMock.methodToMock(anyObject()).andReturn("B object number 1");
expect(objectToMock.methodToMock(anyObject()).andReturn("B object number 2");
Use a comparator
According to your comment, you might actually prefer this
expect(mock.methodToTest(EasyMock.cmp(new A(1), Comparator.comparingInt(A::getValue), LogicalOperator.EQUAL))).andReturn(1);
The only problem is that you need a A with the correct value to compare with.
To have a simplified version, you can use your own matcher
The expectation using the custom matcher right below.
expect(mock.methodToTest(cmp(0))).andReturn(3);
public static <T> T cmp(int value) {
reportMatcher(new IArgumentMatcher() {
#Override
public boolean matches(Object argument) {
return value == ((A) argument).getValue();
}
#Override
public void appendTo(StringBuffer buffer) {
buffer.append("A.value=").append(value);
}
});
return null;
}
When unittesting we verify public observable behavior of the code under test, that is return values and communication with dependencies.
Anything else is implementation detail which we do not test. The reason is that you might want to refactor your code. That means you want to improve the structure of your code without changing its behavior. Your unittest schould verify that you did not change behavior accidentally. But they can only do this if you do not have to change them too.

Mockito mock object with list argument's contents

I've faced this situation pretty often and don't know how to resolve it using Mockito's default methods such as (any, anyList, eq)
For example I have an object where I want to mock a method expecting a list which contains other mocked objects. Let me explain:
public class MyMapper {
public List<DataObjects> convertList(List<String> rawContents) {
rawContents.stream().map(r -> convertObject(r))
.collect(Collectors.toList());
}
public DataObject convertObject(String rawContent) {
return new DataObject(rawContent);
}
}
public class MyWorkerClass {
public boolean start(List<String> rawContents) {
List<DataObject> objects = new MyMapper().convertList(rawContents);
return publish(objects);
}
public boolean result publish(List<DataObject> objects) {
../// some logic
}
}
Now what I want to assert is something like. Note: Please assume the right mocks are returned when new() is called [Using some PowerMockito]
#Test
public void test() {
String content = "content";
DataObject mock1 = Mockito.mock(DataObject.class);
MyMapper mapperMock = Mockito.mock(MyMapper.class);
MyWorkerClass worker = new MyWorkerClass();
Mockito.when(mapperMock.convertObject(content)).thenReturn(mock1);
Mockito.when(worker.publish(eq(Arrays.asList(mock1)).thenReturn(true);
boolean result = worker.start(Arrays.asList(content));
Assert.assertTrue(result);
}
The problem with the code above is in the line
Mockito.when(worker.publish(eq(Arrays.asList(mock1)).thenReturn(true);
This will try to match the list object instead of the list contents, in other words, even when I have to lists A: [mock1] and B: [mock1], A is not equal to B and ultimately the stubbing fails.
What I need is some sort of matcher similar to hamcrest's contain matcher. Something like:
Mockito.when(worker.publish(contains(mock1)).thenReturn(true));
Is there anyway I can achieve this? Keep in mind the code above is just an example to grasp the problem, the real situation is a little bit more complex and I can only mock individual objects, not the list itself
Thanks
Nevermind, later I learned that Mockito's eq() method will call the equals() method on the argument. Now if that is an ArrayList it means it will return true if two list sizes are equal and if the equal's comparison for each one of the elements in the list also returns true. See https://docs.oracle.com/javase/6/docs/api/java/util/List.html#equals%28java.lang.Object%29
And for even more customization argThat() could be used What's the difference between Mockito Matchers isA, any, eq, and same?

Test that a returned string is of a certain length with Mockito

There's a method with several parameters that returns a String. In many conditions the method throws an exception. When it does return, the contents of the String are dependent on both the parameters and on the configuration of a certain USB dongle plugged into the computer. The length of the returned String is entirely dependent on the parameters though.
What I'm wondering is how to unit test this using Mockito (which I'm new to). One of the test methods should complete successfully when the returned String is of a certain length.
Let me know if you need more info.
Having such interface:
interface Foo {
void method(String s);
}
One idea is to use regular expression matching:
final Foo mock = mock(Foo.class);
mock.method("abc");
verify(mock).method(matches(".{3}"));
Unfortunately there is no built-in matcher for string length (there should be!), but it's easy to write custom one:
private static String hasSize(final int size) {
return argThat(new ArgumentMatcher<String>() {
#Override
public boolean matches(Object argument) {
return argument.toString().length() == size;
}
});
}
Now simply call static method:
verify(mock).method(hasSize(4));
Tomasz's Answer is perfectly fine if you want to stick with Hamcrest. Plus he used a method that describe what is the intention instead of inserting the anonymous class in your verification code. +1 for his answer :)
But there's an alternative with FESTAssert library and ArgumentCaptor that could offer many more simple assertions without having to write one, and in a fluent way. When you have many assertions, it becomes kind of uneasy with Hamcrest. So here's what I'm using most of the time :
#RunWith(MockitoJUnitRunner.class)
public class MyTypeTest {
#Mock MyType myType;
#Captor ArgumentCaptor<String> stringCaptor;
#Test public void ensure_method_receive_String_that_has_32_chars() {
// given
...
// when
...
// then
verify(myType).method(stringCaptor.capture());
assertThat(stringCaptor.getValue()).isNotNull().hasSize(32);
}
Hope that helps.
Since Java 8 you can check the length with this one-liner:
verify(yourMock).theMethod(argThat(a -> a.toString().length() == LENGTH));
(using import static org.mockito.ArgumentMatchers.argThat;)

Is it OK to override a Set's contains() method?

I was playing around with Java reflection, and I wanted to create a method caching mechanism from Methods declared in different classes. In order to prevent random behavior, I want to forbid loading methods with the same signature to the Cache (method declared in different classes can have the same signature).
The only way I found of doing this, was to Override the contains() method of the Set where I cache the methods.
Is it dangerous to do so? Do you have any better idea to achieve this?
private final Set<Method> methodsCache;
public MyMethodCachingClass(Set<Class<?>> classes) {
methodsCache = new HashSet<Method>(){
private static final long serialVersionUID = -1467698582662452923L;
/**
* Overwriting the contains method of this Set so that we don't allow multiple methods with the same signature,
* even if they are declared in different classes.
*/
#Override
public boolean contains(Object o) {
if (!(o instanceof Method)) {
return false;
}
Method method = (Method) o;
for (Method m : this) {
if (method.getName().equals(m.getName()) && method.getParameterTypes().equals(m.getParameterTypes())) {
return true;
}
}
return false;
}
};
for (Class<?> c : classes) {
for (Method m : c.getDeclaredMethods()) {
if (methodsCache.contains(m)) {
throw new IllegalArgumentException("The method " + m.getName() + " exists with the same signature in two different classes.");
}
methodsCache.add(m);
}
}
}
Thanks!
Simply use a combination of the following for your cache key:
class name + method name + method parameter types
It is entirely okay to override the "contains()" method - however, it is generally unnecessary. The purpose of the contains() method is simply to check wether an equivalent object already exists in the collection.
The "equals" method for your particular object collection will be used to ascertain this.
However, if you want custom behavior for contains which CANNOT be embedded in the object, it might be worthwhile to hack the contains() method. I think , given the fact that you are trying to cache java Methods, you might want to glue the "contains" logic into the set containing these methods.... However, its not entirely clear.
I agree with the above post (#jayunit100). However, I wouldn't override contains() for this. Instead, I'd write an implementation of Comparator and use a SortedSet. Eg:
SortedSet<Method> cachedMethods = new TreeSet<Method>(new Comparator<Method>() {
// compare() implementation omitted
});

java why should equals method input parameter be Object

I'm going through a book on data structures. Currently I'm on graphs, and the below code is for the vertex part of the graph.
class Vertex<E>{
//bunch of methods
public boolean equals(Object o){
//some code
}
}
When I try to implement this equals method my compiler complains about not checking the type of the parameter and just allowing any object to be sent it. It also does seem a bit strange to me why that parameter shouldn't be a Vertex instead of an Object. Is there a reason why the author does this or is this some mistake or antiquated example?
#Override
public boolean equals(Object obj)
{
if (!(obj instanceof Vertex)) return false;
else return // blah blah
}
equals(Object) is the method defined in the root - Object. If you don't match the signature exactly, Object's version will be called when someone checks if two objects are equal. Not what you want.
You've probably seen other methods (like Comparator) where you can use the exact time. That's because those APIs were generic-ified with Java 5. Equals can't be because it is valid to call equals with two separate types. It should return false, but it is valid.
equals is a method inherited from Object, is defined to be flexible enough so that you can take any object and test if it is equal to any other object (as it rightfully should be able to do), so how could it be any other way?
Edit 1
Comment from jhlu87:
so is it not good form to write an equals method that has an input parameter of vertex?
You are welcome to create your own overload to any method, including equals, but doing so without changing the name could risk confusing many who would assume that your equals is the one that inherits from Object. If it were my code and I wanted a more specific equals method, I'd name it slightly different from just "equals" just to avoid confusion.
If your method doesn't take an argument of type Object, it isn't overriding the default version of equals but rather overloading it. When this happens, both versions exist and Java decides which one to use based on the variable type (not the actual object type) of the argument. Thus, this program:
public class Thing {
private int x;
public Thing(int x) {
this.x = x;
}
public boolean equals(Thing that) {
return this.x == that.x;
}
public static void main(String[] args) {
Thing a = new Thing(1);
Thing b = new Thing(1);
Object c = new Thing(1);
System.out.println(a.equals(b));
System.out.println(a.equals(c));
}
}
confusingly prints true for the first comparison (because b is of type Thing) and false for the second (because c is of type Object, even though it happens to contain a Thing).
It's because this method existed before generics, so for backward compatabitity it has to stay this way.
The standard workaround to impose type is:
return obj instanceof MyClass && <some condition>;
It is because the author is overriding equals. Equals is specified in java.lang.Object and is something that all classes inherrits from.
See the javadoc for java.lang.Object

Categories