I have a test case where I have provided my test data in the form of enums. Like
enum TestTransactions {
TestTransactions(Transaction T1, Transaction T2, String expectedOutput){}
}
In my Test class, I have to use it as:
class Test {
private final static int REPETITION_COUNT = TestTransactions.values().length;
#RepeatedTest(value=REPETITION_COUNT)
private void testAllTransactions(RepetitionInfo info) {
TestTransactions currentTest = TestTransactions.values()[info.getCurrentRepetition()];
logger.info("Executing test for " + currentTest.name());
setExpectationsFor(currentTest);
whenControllerIsCalled();
Assert.assertEquals(currentTest.getExpectedOutput(), result.getBody());
}
}
Here this line #RepeatedTest(value=REPETITION_COUNT) is giving compilation error saying "Attribute value must be constant."
Is there any way to achieve this? Though I have tried assigning REPETITION_COUNT (declared as final) inside constructor and static block as well and during declaration as shown in this example.
If I understand your use case correctly, you want to use #ParameterizedTest with #EnumSource instead of #RepatedTest - this is how JUnit5 supports such use case out of the box.
First, add dependency on org.junit.jupiter:junit-jupiter-params (which provides support for #ParameterizedTest) and then:
class Test {
#ParameterizedTest
#EnumSource
void testAllTransactions(TestTransactions currentTest) {
logger.info("Executing test for " + currentTest.name());
setExpectationsFor(currentTest);
whenControllerIsCalled();
Assertions.assertEquals(currentTest.getExpectedOutput(), result.getBody());
}
}
Also side notes for JUnit 5:
#Test methods should be package-private (no visibility qualifier), not private
use Assertions and not Assert
What you’re experiencing is a constraint of the Java compiler. Unless the Java language specification will be changed you cannot do what you want.
What you could do is make a feature request for Jupiter to also accept a value provider, e.g. of type Class<? extends Supplier<Integer>>. Or you could simulate it using Jupiter‘s dynamic tests feature.
Related
I am just trying to go through with Spock test framework and as per the documentation it looks better than JUnit and tried to write the simple test case but somehow it's not working.
Class:
public class SwapTwoNumber {
public void swapNumber(int a,int b){
a =a+b;
b=a-b;
a=a-b;
}
}
Spock Test Groovy:
import spock.lang.Specification
class SwapTwoNumberTest extends Specification{
def "swap to number as given. #a and #b"() {
given:
int a = 5
int b = 6
when:
SwapTwoNumber sw =Mock()
sw.swapNumber(a,b)
then:
a==6
b==5
}
}
I am not familiar with the testing framework you mentioned but this is most likely not a framework problem. You are passing primitives arguments.
As stated in the docs
Primitive arguments, such as an int or a double, are passed into methods by value. This means that any changes to the values of the parameters exist only within the scope of the method. When the method returns, the parameters are gone and any changes to them are lost.
I have a scenario where I'm trying to disable parameterized tests but I don't want to disable all of them. Consider the below mock scenario
#CsvSource({
"1,1",
"2,2",
"3,not3"
})
#ExtendWith(DisableParameterized.class)
#ParameterizedTest( name = "Test for {0} is equal to {1}")
void equality(String expected, String actual) {
assertEquals(expected, actual, "Not as expected");
}
Is it possible to write a DisableParameterized implements ExecutionCondition which can disable one of the Parameterized tests basis some meta data.
The only proper info I can make out about ParameterizedTests in the
public ConditionEvaluationResult evaluateExecutionCondition(ExtensionContext extensionContext)
is the displayName
Is there some other way to generically provide some sort of meta data to disable the Parameterized tests ?
if I mark them as #Disabled, it disables all of the ParameterizedTests to the point that it doesn't even invoke the individual tests and stops at the method level.
Edit :
As mentioned by Sam below, it's not possible to do it using standard API till the issues mentioned are fixed/implemented and release in a newer version.
I was however able to write a code using reflection to achieve what I wanted, so I've added an answer below
No, as of JUnit Jupiter 5.6 (pending GA release at the time of this writing), there is no way to access the arguments used to invoked a parameterized test.
For the time being, the only way to infer that type of information is indeed via the display name.
The following open JUnit 5 issues are related to this topic.
https://github.com/junit-team/junit5/issues/1139
https://github.com/junit-team/junit5/issues/1668
https://github.com/junit-team/junit5/issues/1884
Currently, till the actual API is implemented under the issues Sam has mentioned, the only way to extract arguments is as below. i.e. using reflection
Which is a dirty way for most people, but that's the only way
Then after extracting the arguments, we can manipulate them however we want.
#Override
public ConditionEvaluationResult evaluateExecutionCondition(ExtensionContext context) {
return disableRequiredOnParameterizedTests(context);
}
private ConditionEvaluationResult disableRequiredOnParameterizedTests(ExtensionContext context) {
// See if my custom annotation is present on the test
var onParameters = findRepeatableAnnotation(context, DisableOnParameter.class);
// Or if the test is a Parameterized test after all
// We have access to arguments when the test will be at method level, i.e. MethodExtensionContext
if (!MethodExtensionContext.class.isAssignableFrom(context.getClass())
|| findAnnotations(context, ParameterizedTest.class).isEmpty()
|| onParameters.isEmpty()) {
return enabled("Not set to disable");
}
List<Object> arguments = getArguments(context);
boolean enable = true;
// Do what you want with arguments
.
.
.
return enable ? enabled("Enabling") ? disabled("Disabling");
}
/**
* A blanket exception safe function to extract arguments from Extension context using
* reflection since we don't have direct access to the API
* https://github.com/junit-team/junit5/issues/1139
*
* #param context needs to be of {#link MethodExtensionContext} type since only at that stage
* arguments can be extracted
* #return List of argument objects, empty if any error occurs
*/
private static List<Object> getArguments(ExtensionContext context) {
try {
if(MethodExtensionContext.class.isAssignableFrom(context.getClass())) {
return emptyList();
}
Method method = findMethod(context.getClass(), "getTestDescriptor").orElseThrow();
TestMethodTestDescriptor descriptor =
(TestMethodTestDescriptor) invokeMethod(method, context);
// Get the TestTemplateInvocationContext
Field templateField = descriptor.getClass().getDeclaredField("invocationContext");
templateField.setAccessible(true);
TestTemplateInvocationContext template =
(TestTemplateInvocationContext) templateField.get(descriptor);
// Get the params finally
Field argumentsField = template.getClass().getDeclaredField("arguments");
argumentsField.setAccessible(true);
return asList((Object[]) argumentsField.get(template));
} catch (Throwable ignored) {
return emptyList();
}
}
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) {
// ...
}
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;
}
}
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.