I am trying to mock an external call along with an ArgumentMatcher to match the input values to the request. But when I trying to fetch the map from the ArgumentMatcher object, it gives me a null value.
Mockito.when(
dynamoDbMapper.scanPage(eq(ABC.class), argThat(new ArgumentMatcher<DynamoDBScanExpression>() {
#Override
public boolean matches(Object argument)
{
DynamoDBScanExpression scanExp = (DynamoDBScanExpression) argument;
Assert.assertEquals("5", scanExp.getLimit());
Assert.assertEquals("xyz",scanExp.getFilterExpression());
Assert.assertEquals(new HashMap(), scanExp.getExpressionAttributeNames());
return true;
}
}))).thenReturn(prepareScanResponse());
This expression scanExp.getExpressionAttributeNames() should ideally return a map but gives me a null value.
So suppose I have to mock a request whose input contains a map, and then try to implement ArgumentMatcher on that inout object which contains a map as an attribute, how would I do that?
Why not use a #Captor? Captors are used to get record parameters passed to methods. It seems like a cleaner way than to try to misuse a matcher.
#ExtendWith(MockitoExtension.class)
class MarketplaceHttpConnectorImplTest {
#Captor
ArgumentCaptor<DynamoDBScanExpression> scanExpressionCaptor;
#Mock
DynamoMapper dynamoDbMapper; // or something like this
#InjectMocks
MyClassToTest sut; // System Under Test
#Test
public void myTest() {
// prepare mocks
when(dynamoDbMapper.scanPage(eq(ABC.class), any(DynamoDBScanExpression.class)).thenReturn(prepareScanResponse());
// Now call the method to test
sut.methodToCall();
// Verify calls
verify(dynamoDbMapper, times(1)).scanPage(eq(ABC.class), scanExpressionCaptor.capture());
DynamoDBScanExpression param = scanExpressionCaptor.getValue();
// now test what was passed to the method.
assertNotNull(param);
// .....
}
}
Btw: don't mind the JUnit5. It also works in JUnit4. Also, I presumed there was just one value. You can capture multiple values in one #Captor and check all values.
Related
I have this method that I need to mock:
public static void myMethod(Supplier<String> supplier, boolean flag) {
// ...
supplier.get();
// ...
}
I need to mock this method which receives a Supplier, and I would like to mock it by just calling the given Supplier. So I did like this:
Mockito.when(myMockedClass.myMethod(ArgumentMatchers.<Supplier<Integer>> any(),
ArgumentMatchers.anyBoolean())).thenAnswer(invocation -> {
Supplier<Integer> command = invocation.getArgument(0);
return command.get();
});
This works, but I am asking if there is a shorter way to do this.
I am studying Mockito and PowerMock recently.
I ran into the following problem
//This method belongs to the Messages class
public static String get(Locale locale, String key, String... args) {
return MessageSupplier.getMessage(locale, key, args);
}
//the new class
#RunWith(PowerMockRunner.class)
#PowerMockIgnore( {"javax.management.*"})
#PrepareForTest({Messages.class, LocaleContextHolder.class})
public class DiscreT {
#Test
public void foo() {
PowerMockito.mockStatic(LocaleContextHolder.class);
when(LocaleContextHolder.getLocale()).thenReturn(Locale.ENGLISH);
PowerMockito.mockStatic(Messages.class);
when(Messages.get(Mockito.any(Locale.class),Mockito.anyString(), Mockito.any(String[].class)))
.thenReturn("123156458");
System.out.print(Messages.get(LocaleContextHolder.getLocale(), "p1"));
System.out.print(Messages.get(LocaleContextHolder.getLocale(), "p1", "p2"));
}
}
the result : null 123156458
why? and how to match the String...
In your first System.out.print statement, you use 2 arguments for the Messages.get method. This is one of the method's overloads that you have not mocked. That's why it returns null. Note that object mocks that have not had their behavior mocked will return null by default.
You would have to mock the Messages.get(Locale, String) method as well if you want it to work
when(Messages.get(Mockito.any(Locale.class),Mockito.anyString()))
.thenReturn("123156458");
Remember, the fact that you have mocked the method that takes the most arguments doesn't mean Mockito understands and mocks the rest of the overloads! You have to mock them as well.
There is no way to mock a method once and automatically mock all of its overloads as far as I know however, there is a way to create a mock object and configure a default response for all of its methods. Check out http://www.baeldung.com/mockito-mock-methods#answer
Approach from mockito 1 not working after updating to 2.3.
private class ArgumentsMatcher implements ArgumentMatcher<Object[]> {
private final Object[] expected;
private ArgumentsMatcher(Object[] expected) {
this.expected = expected;
}
#Override
public boolean matches(Object[] argument) {
return Arrays.equals(expected, argument);
}
}
You can match against it using a captor like this:
// Use an argument captor of whatever type the varargs method is
ArgumentCaptor<String> captor = ArgumentCaptor.forClass(String.class);
// Verify on the method using the captor
verify(fooClass).fooMethod(captor.capture());
// Assert on the expected values
assertEquals(captor.getAllValues(), Arrays.asList("vararg1", "vararg2"));
The nice thing about this is that you can match against arrays (if you're in a case where arrays and varargs can be mixed) also use whatever hamcrest matchers you want so you can do things like verify a single element is present, ignore order, ignore duplicates, or whatever else you need to do.
It looks like the VarargMatcher interface is not needed anymore. I am using Mockito in a Scala project and it appears you can create custom varargs matchers like normal custom matchers but you just treat the argument as a sequence (not sure how it works in Java, I suspect you get an Array or a List).
A matcher checking if the varargs contain a certain element works like this:
import org.mockito.ArgumentMatcher
case class IsVarargsContaining[T](expected: T) extends ArgumentMatcher[T] {
override def matches(arg: T): Boolean =
arg.isInstanceOf[Seq[T]] && arg.asInstanceOf[Seq[T]].contains(expected)
override def toString() = s"<vararg list containing element $expected>"
}
It looks like this is no longer supported in Mockito 2. The alternative to your code would be:
// imagine this was expected
String[] expected = {"A", "B", "C"};
// and this was the method
void call(String ... varArgs);
// here's how you'd verify it
verify(someFunction).call(eq("A"), eq("B"), eq("C"));
// or
verify(someFunction).call(any());
I'm using mockito 2.15 and I ended up with creating a custom Matcher that implements org.mockito.ArgumentMatcher<T> and takes a set of values to match as a constructor argument.
Then I just pass it around:
verify(someone).aMethods(argThat(matcher), argThat(matcher), etc.);
Not great (the number of argThat() calls has to match the args length) but at least it's not order-sensitive...
I've been trying to create a TEMPORARY override on new objects, and then to remove the override on the objects themselves. I'm not sure if this can be done, but here is what I've tried so far.
// Say I have a class like:
class Validator {
boolean validate() { println "code here to return actual true/false"; false }
}
// I have two integration points one of them is Here before construction:
// First integration point:
// Save actual validate function
def realValidate = Validator.&validate
// Make new instances of Validator have the validate function hardwired to true
Validator.metaClass.validate { -> println "hardwired true"; true }
// Code I'd rather not modify
// Now some code executes which news up an instance and calls validate
def validator = new Validator()
validator.validate() // This correctly calls our override
// Second integration point.
// Without newing up a new Validator object, I'd like to remove the override.
Validator.metaClass = null
validator.metaClass.validate = Validator.&validate
// This throws "java.lang.IllegalArgumentException: object is not an instance of declaring class"
//validator.validate()
// So maybe I have to explicitly say:
realValidate.resolveStrategy = Closure.DELEGATE_FIRST
// But this still throws the same exception
//validator.validate()
// Perhaps if I tell my objects metaclass to forget about validate, it will bubble up and look for the method on its declaring class?
validator.metaClass.validate = { -> throw new MissingMethodException("validate", Validator.class, (Object[])[], false) }
// This throws MissingMethodException: No signature of method: Validator.validate() is applicable for argument types: () values: []
// Possible solutions: validate(), wait()
//validator.validate()
Apologies for not having a super specific question, since I don't know what all is possible in this particular area. I'd love both the reason why my code doesn't work, as well as alternatives to make it work.
This could be a per instance meta class problem... Validator.metaClass = null will set the global meta class for the Validator class to default. but your validator instance here is a Groovy class and thus stores a separate reference to the meta class in the instance itself. Calls with that instance will not go through a lookup of the global meta class and instead use the per instance meta class (the reference stored in the instance itself). Thus validator.metaClass = null is the only way to reset this
A small modification to your strategy would be fruitful. Use metaClass on the object instead of the Class.
// Say I have a class like:
class Validator {
boolean validate() { println "code here to return actual true/false"; false }
}
def validator = new Validator()
// mark that the pointer is on object instead of class
def realValidate = validator.&validate
validator.metaClass.validate { -> println "hardwired true"; true }
validator.validate() // This correctly calls our override
// Second integration point.
// DO NOT NEED THIS
// validator.metaClass = null
// Assign the method pointer to validate to call original validate
validator.metaClass.validate = realValidate
validator.validate()
Your approach did not work because you had validate() overridden on the metaClass of Class reference instead of the object itself.
I want to be sure that mocked is called with specific set of strings as parameter.
For example, I have the following code:
public class SomeLogic {
#Autowired
private SpecificService specificService;
public void action() {
Set<String> args = fillArgsMethod();
specificService.handleArgs(args);
}
}
And my current try to test it is the following
#Mock
private SpecificService specificService
#InjectMocks
private SomeLogic someLogic;
#Test
public void testAction() {
someLogic.action();
verify(specificService).handleArgs(anySet());
}
But I want to be sure, that handleArgs() will receive the exact set of strings, that I expect. How can I modify verifying to check that handleArgs is called with set "first","second"?
Thanks
Isah gave a valid answer, but I want to turn your attention to a more general feature of Mockito which is ArgumentCaptor
In you case you would do something along the following lines:
Class<HashSet<String>> setClass = (Class<HashSet<String>>)(Class)HashSet.class;
ArgumentCaptor<Set<String>> setCaptor= ArgumentCaptor.forClass(setClass .class);
verify(specificService).create(setCaptor.capture());
HashSet<String> capturedSet = setCaptor.getValue();
//do whatever test you want with capturedSet
Prepare your Set parameters before calling the test method
#Test
public void testAction() {
Set<String> expectedParams = new HashSet(Arrays.asList("first", "second");
//call tested method
verify(specificService).handleArgs(expectedParams);
}
isah's solution is perfect for you if you want to confirm that the set contains exactly the two items you specify; Mockito compares using .equals by default, and Set.equals is defined as refer to equal elements in any order.
For a more-flexible "contains" test that matches your question title, that allows for set members beyond your expected values, you can also use the Hamcrest contains matcher:
someLogic.action();
verify(specificService).handleArgs(argThat(contains("first", "second")));
At least, that's how it should look. Unfortunately, argThat infers its return type from the Matcher, which infers its return type from the arguments, so Java assumes your first argument is not a Set<String> but a Iterable<capture#1-of ? extends String>. You'll need to cast explicitly and suppress warnings to get it to work:
// requires #SuppressWarnings("unchecked")
verify(specificService).handleArgs(
(Set<String>) argThat(contains("first", "second")));