Testing a custom `ArgumentMatcher` in Mockito involves several steps to ensure it works as expected. Here's a detailed guide on how to effectively test your custom matcher:
Understanding ArgumentMatchers
First, it's essential to understand what `ArgumentMatchers` are. They are used in Mockito to verify that a mock object was called with specific arguments. The `ArgumentMatcher` interface is a functional interface that allows you to define custom matching logic by implementing the `matches(T argument)` method.
Creating a Custom ArgumentMatcher
To create a custom `ArgumentMatcher`, you need to implement the `ArgumentMatcher` interface. This interface requires you to implement two methods: `matches(T argument)` and `describeTo(Description description)`. The `matches(T argument)` method is where you define the logic to match the argument, and the `describeTo(Description description)` method is used to provide a description of the matcher for better error messages.
Here's an example of a simple custom `ArgumentMatcher` that checks if an email object has specific properties:
java
public class EmailArgumentMatcher implements ArgumentMatcher {
private final String to;
private final String subject;
private final int amount;
public EmailArgumentMatcher(String to, String subject, int amount) {
this.to = to;
this.subject = subject;
this.amount = amount;
}
@Override
public boolean matches(Email email) {
boolean toMatches = to.equals(email.getTo());
boolean subjectMatches = subject.equalsIgnoreCase(email.getSubject());
boolean bodyMatches = email.getBody().contains(String.valueOf(amount));
return toMatches && subjectMatches && bodyMatches;
}
@Override
public void describeTo(Description description) {
description.appendText("Email to ").appendValue(to).appendText(", subject ").appendValue(subject).appendText(", and body containing ").appendValue(amount);
}
}
Testing the Custom ArgumentMatcher
To test your custom `ArgumentMatcher`, you can use it within a Mockito test. Here's how you can do it:
1. Setup Your Mocks: First, you need to set up your mocks. For example, if you're testing a service that sends emails, you would mock the email service.
2. Use the Custom Matcher: Use your custom `ArgumentMatcher` with `argThat()` in the `verify()` method to check if the mock was called with the expected arguments.
Here's an example:
java
@Test
public void testEmailService() {
// Arrange
EmailService emailService = mock(EmailService.class);
String to = "
<script language='JavaScript' type='text/javascript'>
<!--
var prefix = 'mailto:';
var suffix = '';
var attribs = '';
var path = 'hr' + 'ef' + '=';
var addy1637 = 'test' + '@';
addy1637 = addy1637 + 'example' + '.' + 'com';
document.write( '<a ' + path + '\'' + prefix + addy1637 + suffix + '\'' + attribs + '>' );
document.write( addy1637 );
document.write( '<\/a>' );
//-->
</script><script language='JavaScript' type='text/javascript'>
<!--
document.write( '<span style=\'display: none;\'>' );
//-->
</script>This e-mail address is being protected from spambots. You need JavaScript enabled to view it
<script language='JavaScript' type='text/javascript'>
<!--
document.write( '</' );
document.write( 'span>' );
//-->
</script>";
String subject = "Test Email";
int amount = 100;
// Act
emailService.sendEmail(new Email(to, subject, "Body with " + amount));
// Assert
verify(emailService, times(1)).sendEmail(argThat(new EmailArgumentMatcher(to, subject, amount)));
}
Alternatively, you can use a lambda expression with `argThat()` for a more concise test:
java
@Test
public void testEmailServiceLambda() {
// Arrange
EmailService emailService = mock(EmailService.class);
String to = "
<script language='JavaScript' type='text/javascript'>
<!--
var prefix = 'mailto:';
var suffix = '';
var attribs = '';
var path = 'hr' + 'ef' + '=';
var addy56701 = 'test' + '@';
addy56701 = addy56701 + 'example' + '.' + 'com';
document.write( '<a ' + path + '\'' + prefix + addy56701 + suffix + '\'' + attribs + '>' );
document.write( addy56701 );
document.write( '<\/a>' );
//-->
</script><script language='JavaScript' type='text/javascript'>
<!--
document.write( '<span style=\'display: none;\'>' );
//-->
</script>This e-mail address is being protected from spambots. You need JavaScript enabled to view it
<script language='JavaScript' type='text/javascript'>
<!--
document.write( '</' );
document.write( 'span>' );
//-->
</script>";
String subject = "Test Email";
int amount = 100;
// Act
emailService.sendEmail(new Email(to, subject, "Body with " + amount));
// Assert
verify(emailService, times(1)).sendEmail(argThat(email -> {
boolean toMatches = to.equals(email.getTo());
boolean subjectMatches = subject.equalsIgnoreCase(email.getSubject());
boolean bodyMatches = email.getBody().contains(String.valueOf(amount));
return toMatches && subjectMatches && bodyMatches;
}));
}
Tips for Effective Testing
- Use Descriptive Names: Use descriptive names for your custom matchers to make the test code more readable.
- Test Edge Cases: Ensure you test edge cases, such as null values or unexpected input types.
- Use `describeTo()` Effectively: Implement `describeTo()` to provide clear error messages when the matcher fails.
By following these steps and tips, you can effectively test your custom `ArgumentMatcher` and ensure it works as expected in your Mockito tests.
Citations:[1] https://ath3nd.wordpress.com/2013/10/21/writing-custom-mockito-matchers-to-match-on-an-attribute-value/
[2] http://www.bennorthrop.com/Snippets/verfiying-parameters-of-mocks-with-mockito.php
[3] https://www.softwaretestinghelp.com/mockito-matchers/
[4] https://coderanch.com/t/96262/engineering/easymock-problems-custom-Argument-Matcher
[5] https://www.lambdatest.com/automation-testing-advisor/selenium/methods/org.mockito.ArgumentMatchers.argThat
[6] https://www.lambdatest.com/automation-testing-advisor/selenium/methods/org.mockito.ArgumentMatchers.matches
[7] https://kenkousen.substack.com/p/tales-from-the-jar-side-a-mockito
[8] https://stackoverflow.com/questions/73046134/when-should-i-use-argument-matcher-or-passing-argument-directly-when-mockito-uni
[9] https://www.digitalocean.com/community/tutorials/mockito-argument-matchers-any-eq