I have a rather complex java function that I want to test using jUnit and I am using Mockito for this purpose. This function looks something like this:
public void myFunction (Object parameter){
...
doStuff();
...
convert(input,output);
...
parameter.setInformationFrom(output);
}
The function convert sets the attributes of output depending on input and it's a void type function, although the "output" parameter is what is being used as if it were returned by the function. This convert function is what I want to mock up as I don't need to depend on the input for the test, but I don't know how to do this, as I am not very familiar with Mockito.
I have seen basic cases as when(something).thenReturn(somethingElse)
or the doAnswer method which I understand is similar to the previous one but more logic can be added to it, but I don't think these cases are appropriate for my case, as my function does not have a return statement.
If you want the mocked method to call a method on (or otherwise alter) a parameter, you'll need to write an Answer as in this question ("How to mock a void return method affecting an object").
From Kevin Welker's answer there:
doAnswer(new Answer() {
Object answer(InvocationOnMock invocation) {
Object[] args = invocation.getArguments();
((MyClass)args[0]).myClassSetMyField(NEW_VALUE);
return null; // void method, so return null
}
}).when(mock).someMethod();
Note that newer best-practices would have a type parameter for Answer, as in Answer<Void>, and that Java 8's lambdas can compress the syntax further. For example:
doAnswer(invocation -> {
Object[] args = invocation.getArguments();
((MyClass)args[0]).myClassSetMyField(NEW_VALUE);
return null; // void method in a block-style lambda, so return null
}).when(mock).someMethod();
Related
I want to mock the getUserDataFromExt() and pass the local hashmap and expecting it to return some list of data and also add/assign some values to the passed in hashmap.
Note: I cannot inject that hashmap from constructor and mock it.
public List<Report> getUserData() {
.............
..............
Map<String, Set<Integer>> studyToRelationshipPk = new HashMap<>();
List<NetworkUserSiteDetail> uniqueList = getUserDataFromExt(studyToRelationshipPk);
..............
}
Is there a way that i can mock the method and still get the data from passed in local argument and return some list.
If you can't refactor your code, you will need to create a spy for your class (to mock for getUserDataFromExt).
Then you can use thenAnswer to modify your HashMap and return the list:
when(spy.getUserDataFromExt()).thenAnswer(
new Answer() {
public Object answer(InvocationOnMock invocation) {
// get your method arguments
Object[] args = invocation.getArguments();
// do whatever with your hashmap
// return your list
return ...
}
}
);
If you can refactor your code, it would probably be better to move the method getUserDataFromExt to a different method and mock it. You could still use the same way to modify both the parameter and the result.
You'll also might want to consider changing the behaviour of the method - as modifying the parameter and return a result - can be quite unexpected from the view of others developers.
Mockito is hard to use when we need to mock overloaded methods when one of them is using varargs. Consider the below methods from Spring's RestTemplate
void put(String url, Object request, Object... uriVariables) throws RestClientException;
void put(String url, Object request, Map<String, ?> uriVariables) throws RestClientException;
Mocking the second one is straight forward, but mocking the first one is not possible as using any() would result in an ambiguous method call matching both the methods and there is no alternative to match just Object...
Sharing the solution as Q & A that I arrived after putting some effort so as to help those in the same boat. All other alternatives welcome.
Solution to this can be attempted by making use of the feature to provide a defaultAnswer to the mock. The defaultAnswer will evaluate that the invocation is for the specific method and perform the needed action, and let the invocation follow the natural flow if the required method is not targeted.
This can be explained well with an example. Consider the two overloaded methods in the class below:
public class StringConcat {
public String concatenate(int i, String... strings) {
return i + Arrays.stream(strings).collect(Collectors.joining(","));
}
public String concatenate(int i, List<String> strings) {
return i + strings.stream().collect(Collectors.joining(","));
}
}
The second method can be mocked using Mockito like below:
StringConcat stringConcat = mock(StringConcat.class);
when(stringConcat.concatenate(anyInt(), anyList())).thenReturn("hardcoded value");
To represent varargs, we do not have anyVararg() method (deprecated and does not work, not sure if it worked in older versions). But the same can be handled by creating the mock with defaultAnswer like below:
#Test
void testWithDefaultAnswer(){
// Creating mock object with default answer
StringConcat stringConcat = mock(StringConcat.class, invocation -> {
Method method = invocation.getMethod();
if (method.getName().contains("concatenate") &&
method.getParameters()[method.getParameters().length-1].isVarArgs()){
if(invocation.getArguments().length>=method.getParameterCount()){
List varArgParams = Arrays.stream(invocation.getArguments())
.skip(method.getParameterCount()-1)
.collect(Collectors.toList());
return invocation.getArguments()[0]+":"
+varArgParams.toString(); // mocked result when varargs provided
}
return ""+invocation.getArguments()[0]; // mocked result when varargs not provided
}
return Answers.RETURNS_DEFAULTS.answer(invocation); // Ensures seamless mocking of any other methods
});
// Mock any non varargs methods as needed
when(stringConcat.concatenate(anyInt(), anyList())).thenReturn("hardcoded"); // mocking as usual
// Test the mocks
System.out.println(stringConcat.concatenate(1, "a", "b")); // default answer based mock, with varargs provided
System.out.println(stringConcat.concatenate(1)); // default answer based mock, without varargs provided
System.out.println(stringConcat.concatenate(1, Lists.newArrayList("a", "b"))); // mocked non varargs method
}
Output:
1:[a, b]
1
hardcoded
This might sound like a very easy question but I am really struggling to archive the solution.
Normally I mock and match quite easily my arguments.
Now I am matching a method that is like this:
getAppFacebookClient(page, V2_11).publish(destination, JsonObject.class, parameters.asArray());
this is for a facebook application and the parameters is a list of a custom Object. the asArray[] method was created in the class and basically does something like this:
public Parameter[] asArray() {
return parameters.toArray(new Parameter[parameters.size()]);
}
and the Parameter of this return is of the type com.restfb.Parameter
So, I am basically doing this
when(client.publish(anyString(), eq(JsonObject.class), any(com.restfb.Parameter[].class))).thenReturn(result);
but seems like it is never taken and of course I cannot manipulate result,
Any idea how could I mock this kind of objects in a proper way?
I also tried the other way
doReturn(result).when(client).publish(anyString(), eq(JsonObject.class), any(com.restfb.Parameter[].class));
Your code is correct ... unless the publish uses varargs!
In such a case you need to use any() / anyVararg() matcher.
Consider:
#Mock Thingy testee;
interface Thingy {
int f(String... arg);
}
#Test
public void test() {
// given
// works only if signature is `f(String[] arg)`
// when(this.testee.f(Mockito.any(String[].class))).thenReturn(42);
when(this.testee.f(Mockito.any())).thenReturn(42); // anyVararg() is deprecated
// when
final int result = this.testee.f(new String[] { "hello", "world" });
// then
assertThat(result, comparesEqualTo(42));
// works only if signature is `f(String[] arg)`
// verify(this.testee).f(Mockito.any(String[].class));
verify(this.testee).f(Mockito.any());
}
well I'm wondering if it's possible to have a method where another method is passed as a parameter, so the first method can call the method passed in param?
Like for instance:
public void goToVisitManagementForm() throws ParseException {
if (isAuthenticated() && userTypeIs("Patient")) {
// I could have this whole block just moved to another method?
Panel newPanel = new Panel("Choose the details for your visit");
Component visitManagementForm = new VisitManagementForm(userData,
this);
newPanel.addComponent(visitManagementForm);
mainWindow.setMainPanel(newPanel);
} else {
authenticate();
}
}
If the code block would be moved to another method and it would be passed as a parameter to this method. How can I achieve that and is this a good practice? Because in this case I have the ifs that I always need to paste in...
What about other aspects of this?
This is called a higher-order function and you cannot do this in Java 7 or below. You can simulate passing functions to other functions through the use of an anonymous class that instantiates some interface the function expects, and then calling the function on that object.
For example, to pass a no-arg function:
interface Function {
void apply();
}
void takesAFunction(Function function) {
function.apply();
}
Then the following code snippet would do what you want:
Function myFunction = new Function() {
#Override
public void apply() {
// your code here.
}
};
takesAFunction(myFunction);
As a side note, reflection is extreme overkill for this type of problem.
You can pass methods as parameters using Java Reflection API.
First, you get a method object from a class:
Class c = MyClass.class;
Method[] methods = c.getMethods();
Method m = // choose the method you want
Then your function can take a Method object as a parameter:
public void aFunction(MyClass o, Method m);
And then inside that function you can invoke the method:
m.invoke(o);
This is a very simple example, where the method doesn't take any parameters. It's pretty easy to expand on this example and add the parameters as well.
Yes, but it is a very advanced procedure. You need to use the Method object. Here is the javadoc on Method:
here is the javadoc:
- http://docs.oracle.com/javase/6/docs/api/java/lang/reflect/Method.html
If I am understanding your question correctly, you want to be able to pass a method as a parameter. There really is no 'smooth' way to do this in Java. In objective C, it is built right into the language, (#selector tag)
I have a method that gets called twice, and I want to capture the argument of the second method call.
Here's what I've tried:
ArgumentCaptor<Foo> firstFooCaptor = ArgumentCaptor.forClass(Foo.class);
ArgumentCaptor<Foo> secondFooCaptor = ArgumentCaptor.forClass(Foo.class);
verify(mockBar).doSomething(firstFooCaptor.capture());
verify(mockBar).doSomething(secondFooCaptor.capture());
// then do some assertions on secondFooCaptor.getValue()
But I get a TooManyActualInvocations Exception, as Mockito thinks that doSomething should only be called once.
How can I verify the argument of the second call of doSomething?
I think it should be
verify(mockBar, times(2)).doSomething(...)
Sample from mockito javadoc:
ArgumentCaptor<Person> peopleCaptor = ArgumentCaptor.forClass(Person.class);
verify(mock, times(2)).doSomething(peopleCaptor.capture());
List<Person> capturedPeople = peopleCaptor.getAllValues();
assertEquals("John", capturedPeople.get(0).getName());
assertEquals("Jane", capturedPeople.get(1).getName());
Since Mockito 2.0 there's also possibility to use static method Matchers.argThat(ArgumentMatcher). With the help of Java 8 it is now much cleaner and more readable to write:
verify(mockBar).doSth(argThat((arg) -> arg.getSurname().equals("OneSurname")));
verify(mockBar).doSth(argThat((arg) -> arg.getSurname().equals("AnotherSurname")));
If you're tied to lower Java version there's also not-that-bad:
verify(mockBar).doSth(argThat(new ArgumentMatcher<Employee>() {
#Override
public boolean matches(Object emp) {
return ((Employee) emp).getSurname().equals("SomeSurname");
}
}));
Of course none of those can verify order of calls - for which you should use InOrder :
InOrder inOrder = inOrder(mockBar);
inOrder.verify(mockBar).doSth(argThat((arg) -> arg.getSurname().equals("FirstSurname")));
inOrder.verify(mockBar).doSth(argThat((arg) -> arg.getSurname().equals("SecondSurname")));
Please take a look at mockito-java8 project which makes possible to make calls such as:
verify(mockBar).doSth(assertArg(arg -> assertThat(arg.getSurname()).isEqualTo("Surname")));
If you don't want to validate all the calls to doSomething(), only the last one, you can just use ArgumentCaptor.getValue(). According to the Mockito javadoc:
If the method was called multiple times then it returns the latest captured value
So this would work (assumes Foo has a method getName()):
ArgumentCaptor<Foo> fooCaptor = ArgumentCaptor.forClass(Foo.class);
verify(mockBar, times(2)).doSomething(fooCaptor.capture());
//getValue() contains value set in second call to doSomething()
assertEquals("2nd one", fooCaptor.getValue().getName());
You can also use #Captor annotated ArgumentCaptor. For example:
#Mock
List<String> mockedList;
#Captor
ArgumentCaptor<String> argCaptor;
#BeforeTest
public void init() {
//Initialize objects annotated with #Mock, #Captor and #Spy.
MockitoAnnotations.initMocks(this);
}
#Test
public void shouldCallAddMethodTwice() {
mockedList.add("one");
mockedList.add("two");
Mockito.verify(mockedList, times(2)).add(argCaptor.capture());
assertEquals("one", argCaptor.getAllValues().get(0));
assertEquals("two", argCaptor.getAllValues().get(1));
}
With Java 8's lambdas, a convenient way is to use
org.mockito.invocation.InvocationOnMock
when(client.deleteByQuery(anyString(), anyString())).then(invocationOnMock -> {
assertEquals("myCollection", invocationOnMock.getArgument(0));
assertThat(invocationOnMock.getArgument(1), Matchers.startsWith("id:"));
}
First of all: you should always import mockito static, this way the code will be much more readable (and intuitive) - the code samples below require it to work:
import static org.mockito.Mockito.*;
In the verify() method you can pass the ArgumentCaptor to assure execution in the test and the ArgumentCaptor to evaluate the arguments:
ArgumentCaptor<MyExampleClass> argument = ArgumentCaptor.forClass(MyExampleClass.class);
verify(yourmock, atleast(2)).myMethod(argument.capture());
List<MyExampleClass> passedArguments = argument.getAllValues();
for (MyExampleClass data : passedArguments){
//assertSometing ...
System.out.println(data.getFoo());
}
The list of all passed arguments during your test is accessible via the argument.getAllValues() method.
The single (last called) argument's value is accessible via the argument.getValue() for further manipulation / checking or whatever you wish to do.