Home Arrow Icon Knowledge base Arrow Icon Global Arrow Icon How can I test my custom ArgumentMatcher effectively


How can I test my custom ArgumentMatcher effectively


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 = 'm&#97;&#105;lt&#111;:';
 var suffix = '';
 var attribs = '';
 var path = 'hr' + 'ef' + '=';
 var addy1637 = 't&#101;st' + '&#64;';
 addy1637 = addy1637 + '&#101;x&#97;mpl&#101;' + '&#46;' + 'c&#111;m';
 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 = 'm&#97;&#105;lt&#111;:';
 var suffix = '';
 var attribs = '';
 var path = 'hr' + 'ef' + '=';
 var addy56701 = 't&#101;st' + '&#64;';
 addy56701 = addy56701 + '&#101;x&#97;mpl&#101;' + '&#46;' + 'c&#111;m';
 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