JUnit 5, how to get parameterized test parameter from BeforeEach callback? - java

I have the following set of methods in different classes:
#ParameterizedTest
#MethodSource("com.myapp.AppleProvider#getApplesDependingOnConditions")
public void testSomething(Apple apple) {
SomeContainer.getInstance().setApple(apple)
// ...
}
The problem is that I cannot avoid copy/pase of the following
name argument for each test call
The very first line of each test - SomeContainer.getInstance().setApple(apple)
I tried to use extension points - BeforeTestExecutionCallback and BeforeEachCallback, but they don't seem to have ability to get parameter with which they are being called.
According to https://github.com/junit-team/junit5/issues/1139 and https://github.com/junit-team/junit5/issues/944 it's not possible to access argument passed to test from extension points yet and parameterized tests don't work for BeforeEach callbacks.
So I'm basically looking for any workaround so that my test could look like:
#MyAwesomeTest
public void testSomething() {
// ...
}
Where #MyAwesomeTest encapsulates two annotations above.
What I've already found:
In extension points the following data is available: displayname, method or tags. If I pass argument into the each test method (though it's very undesirable) looks like I can rely on displayname since it'll reflect argument passed to the method call for a particular parameter.
I'm trying to find out whether there're any other ways without need to add argument into each method.

I think you could cheat to get most of the way there:
public static Stream<String> apples() {
return com.myapp.AppleProvider
.getApplesDependingOnConditions()
.stream()
.peek(apple -> SomeContainer.getInstance().setApple(apple))
.map(apple -> { /* convert to name string */ })
}
#ParameterizedTest
#MethodSource("apples")
public void testSomething(String name) {
// ...
}

Related

Invalid use of argument matchers. How can I test it, if this method use another private method?

I have method:
public void loadPlatformDependencies() {
try {
dependenciesRepository.deleteAll();
dependenciesRepository.saveAll(pullLastDependencies());
publisher.publishEvent(new LoadedDependenciesEvent());
} catch (Exception e) {
LOGGER.error("Failed to load dependencies", e);
}
}
And I try to test it:
#Test
public void testLoadPlatformDependencies() {
ArgumentCaptor<Iterable<Dependency>> captor = ArgumentCaptor.forClass(Iterable.class);
when(dependenciesRepository.saveAll(captor.capture())).thenReturn(any(Iterable.class));
puller.loadPlatformDependencies();
verify(dependenciesRepository,times(1)).deleteAll();
verify(dependenciesRepository, times(1)).saveAll(any(Iterable.class));
verify(publisher,times(1)).publishEvent(any());
}
But there is a problem, that method pullLastDependencies() work incorect now. I have a mistake:
Invalid use of argument matchers!
0 matchers expectd, 1 recorded:
Method pullLastDependencies() returns List.
Can I test this method without a properly working method pullLastDependencies()? Or maybe I should test this method in another way?
You're using the captor in when() instead of verify().
And you're returning any() (which is just null) from your mocked method, instead of returning what you want this mock to return. if you don't care about what it returns because you don't use it, then return an empty iterable.
It should be
when(dependenciesRepository.saveAll(any()).thenReturn(Collections.emptyList());
puller.loadPlatformDependencies();
verify(dependenciesRepository).deleteAll();
verify(dependenciesRepository).saveAll(captor.capture());
I think the problem here is that you are using a matcher as a return value in
when(dependenciesRepository.saveAll(captor.capture())).thenReturn(any(Iterable.class));
You should use the matchers to "match" method parameters, and return another structure, like this:
when(dependenciesRepository.saveAll(anyIterable())).thenReturn(Collections.emptyList())
As long as your pullLastDependencies() method doesn't have another dependency, it should work.
Edit:
It seems that your pullLastDependencies() has some other dependencies, so you need to mock the call to it. You can achieve this by changing the visibility of the method during the test, so you can mock it, but keep in mind that this is not considered good pratice.
//making private method accessible
Method method = service.getClass().getDeclaredMethod("pullLastDependencies",params);
method .setAccessible(true);
when(pullLastDependencies()).thenReturn(Collections.emptyList())

Mockito - Mocking overloaded methods with Varargs parameters

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

how to intercept parameter with mockito for a normal array of objects?

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());
}

Mockito: InvalidUseOfMatchersException

I have a command line tool that performs a DNS check. If the DNS check succeeds, the command proceeds with further tasks. I am trying to write unit tests for this using Mockito. Here's my code:
public class Command() {
// ....
void runCommand() {
// ..
dnsCheck(hostname, new InetAddressFactory());
// ..
// do other stuff after dnsCheck
}
void dnsCheck(String hostname, InetAddressFactory factory) {
// calls to verify hostname
}
}
I am using InetAddressFactory to mock a static implementation of the InetAddress class. Here's the code for the factory:
public class InetAddressFactory {
public InetAddress getByName(String host) throws UnknownHostException {
return InetAddress.getByName(host);
}
}
Here's my unit test case:
#RunWith(MockitoJUnitRunner.class)
public class CmdTest {
// many functional tests for dnsCheck
// here's the piece of code that is failing
// in this test I want to test the rest of the code (i.e. after dnsCheck)
#Test
void testPostDnsCheck() {
final Cmd cmd = spy(new Cmd());
// this line does not work, and it throws the exception below:
// tried using (InetAddressFactory) anyObject()
doNothing().when(cmd).dnsCheck(HOST, any(InetAddressFactory.class));
cmd.runCommand();
}
}
Exception on running testPostDnsCheck() test:
org.mockito.exceptions.misusing.InvalidUseOfMatchersException:
Invalid use of argument matchers!
2 matchers expected, 1 recorded.
This exception may occur if matchers are combined with raw values:
//incorrect:
someMethod(anyObject(), "raw String");
When using matchers, all arguments have to be provided by matchers.
For example:
//correct:
someMethod(anyObject(), eq("String by matcher"));
Any input on how to solve this?
The error message outlines the solution. The line
doNothing().when(cmd).dnsCheck(HOST, any(InetAddressFactory.class))
uses one raw value and one matcher, when it's required to use either all raw values or all matchers. A correct version might read
doNothing().when(cmd).dnsCheck(eq(HOST), any(InetAddressFactory.class))
I had the same problem for a long time now, I often needed to mix Matchers and values and I never managed to do that with Mockito.... until recently !
I put the solution here hoping it will help someone even if this post is quite old.
It is clearly not possible to use Matchers AND values together in Mockito, but what if there was a Matcher accepting to compare a variable ? That would solve the problem... and in fact there is : eq
when(recommendedAccessor.searchRecommendedHolidaysProduct(eq(metas), any(List.class), any(HotelsBoardBasisType.class), any(Config.class)))
.thenReturn(recommendedResults);
In this example 'metas' is an existing list of values
It might help some one in the future: Mockito doesn't support mocking of 'final' methods (right now). It gave me the same InvalidUseOfMatchersException.
The solution for me was to put the part of the method that didn't have to be 'final' in a separate, accessible and overridable method.
Review the Mockito API for your use case.
May be helpful for somebody. Mocked method must be of mocked class, created with mock(MyService.class)
For my case, the exception was raised because I tried to mock a package-access method. When I changed the method access level from package to protected the exception went away. E.g. inside below Java class,
public class Foo {
String getName(String id) {
return mMap.get(id);
}
}
the method String getName(String id) has to be AT LEAST protected level so that the mocking mechanism (sub-classing) can work.
Inspite of using all the matchers, I was getting the same issue:
"org.mockito.exceptions.misusing.InvalidUseOfMatchersException:
Invalid use of argument matchers!
1 matchers expected, 3 recorded:"
It took me little while to figure this out that the method I was trying to mock was a static method of a class(say Xyz.class) which contains only static method and I forgot to write following line:
PowerMockito.mockStatic(Xyz.class);
May be it will help others as it may also be the cause of the issue.
Another option is to use a captor: https://www.baeldung.com/mockito-argumentcaptor
// assume deliver takes two values
#Captor
ArgumentCaptor<String> address; // declare before function call.
Mockito.verify(platform).deliver(address.capture(), any());
String value = address.getValue();
assertEquals(address == "some#thing.com");
Captors are especially useful if say one member of the object you want to capture could be a random ID and another is something you can validate against.
Do not use Mockito.anyXXXX(). Directly pass the value to the method parameter of same type.
Example:
A expected = new A(10);
String firstId = "10w";
String secondId = "20s";
String product = "Test";
String type = "type2";
Mockito.when(service.getTestData(firstId, secondId, product,type)).thenReturn(expected);
public class A{
int a ;
public A(int a) {
this.a = a;
}
}

Can Mockito capture arguments of a method called multiple times?

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.

Categories

Resources