I have created a custom Predicate
below and want to test it using mockito
. I am creating the mocks of the specific exception classes since these dont have public constructor. After running the test assert is failing since the predicate
is returning false
instead of true
. On printing the class
of the mocked exception it has WebClientResponseException$ServiceUnavailable$MockitoMock$54675
.Seems like the mock is not recognized correctly. Am I doing something wrong here?
PredicateTest
@ExtendsWith(MockitoExtention.class)
class PredicateTest{
@InjectMocks
CustomPredicate customPredicate;
@Test
public void testPredicate(){
final ServiceUnavailable serviceUnavailable = mock(ServiceUnAvailable.class);
assertTrue(customPredicate.test(serviceUnavailable))
}
}
CustomPredicate
CustomPredicate implements Predicate<Throwable>{
private static final List<Class<?>> Exceptions= Arrays.asList(WebClientResponseException.ServiceUnavailable.class);
private static final Predicate<? super Throwable> ClassToControl= throwable -> Exception.contain(throwable.getClass());
@Override
public boolean test(Throwable t){
return ExceptionUtils.getThrowableList(t).stream().anyMatch(ClassToControl);
}
}
CodePudding user response:
Actually the problem is in mock(ServiceUnAvailable.class)
- when you create an object this way it will have a class ServiceUnAvailable$MockitoMock
, not a ServiceUnAvailable.class
, it means that the next your check fill fail:
Predicate<? super Throwable> ClassToControl= throwable -> Exception.contain(throwable.getClass());
Because Exceptions
list doesn’t contain element ServiceUnAvailable$MockitoMock
.
In order to test exceptions this way I would suggest the next fix (I changed the code a little bit, but I hope the idea is clear):
Predicate:
public class CustomPredicate implements Predicate<Throwable> {
private final List<Class<?>> exceptions;
private final Predicate<? super Throwable> classToControl;
public CustomPredicate(Class<?>... exceptions) {
this(Arrays.asList(exceptions));
}
public CustomPredicate(List<Class<?>> exceptions) {
this.exceptions = exceptions;
this.classToControl = throwable -> this.exceptions.contains(throwable.getClass());
}
@Override
public boolean test(final Throwable throwable) {
return ExceptionUtils.getThrowableList(throwable).stream()
.anyMatch(classToControl);
}
}
Test:
public class PredicateTest {
@Test
public void testPredicate() {
final IllegalStateException serviceUnavailable = Mockito.mock(IllegalStateException.class);
CustomPredicate customPredicate = new CustomPredicate(serviceUnavailable.getClass());
assertTrue(customPredicate.test(serviceUnavailable));
}
}
CodePudding user response:
@VolodyaLombrozo correctly identified the root cause of the problem:
var serviceUnavailableMock = mock(ServiceUnavailable.class);
System.out.println(serviceUnavailableMock.getClass());
System.out.println(serviceUnavailableMock.getClass() == ServiceUnavailable.class);
System.out.println(serviceUnavailableMock instanceof ServiceUnavailable);
// class org.example.ServiceUnavailable$MockitoMock$X21NGyAU
// false
// true
On top of his answer, I'd like to suggest more options to change your code:
Let's refactor CustomPredicate:
- test() converts wraps input into 1-element list, converts it to a stream, and runs a test with anyMatch. This is confusing and unnecessary.
public class CustomPredicate implements Predicate<Throwable> {
private static final List<Class<?>> Exceptions = List.of(ServiceUnavailable.class);
@Override
public boolean test(Throwable t) {
return Exceptions.contains(t.getClass());
}
}
Fix the test
You have 2 options, depending if you want your predicate to pass on subclasses:
- pass actual instance of ServiceUnavailable to test() (use new instead of mocking)
- use instanceof check in test (using stream and anyMatch, but on Exceptions list)
All of this assumes that this is dummy code and your Exceptions list is longer. If you want to test your throwable against one class, a lambda would feel pretty adequate:
Predicate<Throwable> pThrowable1 = t -> t instanceof ServiceUnavailable; // if you care about subclasses
Predicate<Throwable> pThrowable2 = t -> ServiceUnavailable.class.equals(t.getClass()); // if you want strict equality