Home Arrow Icon Knowledge base Arrow Icon Global Arrow Icon What are some advanced examples of using custom matchers in Mockito


What are some advanced examples of using custom matchers in Mockito


Custom matchers in Mockito are powerful tools that allow you to define specific matching logic for arguments when stubbing or verifying methods. Here are some advanced examples of using custom matchers:

1. Matching on Specific Attributes

Custom matchers can be used to match specific attributes of an object. This is particularly useful when you want to verify that a method was called with an object having certain properties.

java
// Example of a custom matcher that matches a specific attribute
public class PersonMatcher implements ArgumentMatcher {
    private final String expectedName;

    public PersonMatcher(String expectedName) {
        this.expectedName = expectedName;
    }

    @Override
    public boolean matches(Person person) {
        return person.getName().equals(expectedName);
    }

    @Override
    public String toString() {
        return "Person with name " + expectedName;
    }
}

// Using the custom matcher
Person person = mock(Person.class);
when(service.process(argThat(new PersonMatcher("John")))).thenReturn(true);

// Verify that the method was called with a person named "John"
verify(service).process(argThat(new PersonMatcher("John")));

2. Combining Multiple Conditions

You can create custom matchers that combine multiple conditions using logical operations. Mockito provides methods like `and()`, `or()`, and `not()` in the `AdditionalMatchers` class to combine matchers.

java
// Example of combining matchers
when(passwordEncoder.encode(or(eq("1"), contains("a")))).thenReturn("ok");

// This will match if the string is either "1" or contains "a"
assertEquals("ok", passwordEncoder.encode("1"));
assertEquals("ok", passwordEncoder.encode("123abc"));

However, for more complex logic, you might need to implement a custom matcher that encapsulates these conditions.

3. Matching Complex Data Structures

When dealing with complex data structures like lists or maps, you might want to match specific elements or keys. Here’s an example of matching a list without considering the order of elements:

java
// Example of a custom matcher for lists without order
class ListWithoutOrderMatcher implements Matcher> {
    private final List expectedList;

    public ListWithoutOrderMatcher(List expectedList) {
        this.expectedList = expectedList;
    }

    @Override
    public boolean matches(List actualList) {
        if (actualList == null) return false;

        Map expectedCounts = countElements(expectedList);
        Map actualCounts = countElements(actualList);

        return expectedCounts.equals(actualCounts);
    }

    private Map countElements(List list) {
        Map counts = new HashMap();
        for (T element : list) {
            counts.put(element, counts.getOrDefault(element, 0) + 1);
        }
        return counts;
    }

    @Override
    public String toString() {
        return "List without order: " + expectedList;
    }
}

// Using the custom matcher
when(service.process(argThat(new ListWithoutOrderMatcher(Arrays.asList("a", "b"))))).thenReturn(true);

// Verify that the method was called with a list containing "a" and "b" in any order
verify(service).process(argThat(new ListWithoutOrderMatcher(Arrays.asList("b", "a"))));

4. Using Lambda Expressions

You can create custom matchers using lambda expressions for more concise code. This approach is useful for simple conditions.

java
// Example of a custom matcher using lambda expression
ArgumentMatcher hasLuck = file -> file.getName().endsWith("luck");

when(fileFilter.accept(argThat(hasLuck))).thenReturn(true);

// Verify that the method was called with a file ending with "luck"
assertTrue(fileFilter.accept(new File("/path/to/luck")));

Conclusion

Custom matchers in Mockito provide a flexible way to define complex matching logic for arguments in tests. They can be used to match specific attributes, combine multiple conditions, handle complex data structures, and even leverage lambda expressions for concise definitions. By implementing custom matchers, you can write more robust and targeted tests that accurately reflect the behavior of your application.

Citations:
[1] https://www.toptal.com/java/a-guide-to-everyday-mockito
[2] https://www.digitalocean.com/community/tutorials/mockito-argument-matchers-any-eq
[3] https://mockk.io
[4] https://kenkousen.substack.com/p/tales-from-the-jar-side-a-mockito
[5] https://www.softwaretestinghelp.com/mockito-matchers/
[6] https://discuss.kotlinlang.org/t/how-to-use-mockito-with-kotlin/324
[7] https://dzone.com/articles/mockito-custom-answers
[8] https://ath3nd.wordpress.com/2013/10/21/writing-custom-mockito-matchers-to-match-on-an-attribute-value/
[9] https://www.lambdatest.com/automation-testing-advisor/selenium/methods/org.mockito.ArgumentMatchers.matches