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);
}
}
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));
}
}
@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:
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:
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
Update: mockito-inline
If you use mockito-inline , it changes the way the mocks are constructed:
This alternative mock maker which uses a combination of both Java instrumentation API and sub-classing rather than creating a new class to represent a mock.
InlineByteBuddyMockMaker javadoc says:
This mock maker will make a best effort to avoid subclass creation when creating a mock. Otherwise it will use the
org.mockito.internal.creation.bytebuddy.SubclassByteBuddyMockMaker
to create the mock class. That means that the following condition is trueclass Foo { } assert mock(Foo.class).getClass() == Foo.class;
unless any of the following conditions is met, in such case the mock maker fall backs to the the creation of a subclass.
- the type to mock is an abstract class.
- the mock is set to require additional interfaces.
- the mock is explicitly set to support serialization
Thus, if you use mockito-inline the test passes, as there is no subclass created:
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
// true
// true
So the issue was I was not having mockito-inline jar in the pom. Not sure why but this solved the issue
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.