简体   繁体   English

使用模拟异常测试自定义谓词由于不正确的模拟而失败 class

[英]Testing custom predicate using mocked Exception failing due to incorrect Mock class

I have created a custom Predicate below and want to test it using mockito .我在下面创建了一个自定义Predicate并想使用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 .运行测试后断言失败,因为predicate返回false而不是true On printing the class of the mocked exception it has WebClientResponseException$ServiceUnavailable$MockitoMock$54675 .Seems like the mock is not recognized correctly.在打印模拟异常的class时,它有WebClientResponseException$ServiceUnavailable$MockitoMock$54675 。似乎没有正确识别模拟。 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:实际上问题出在mock(ServiceUnAvailable.class) - 当你以这种方式创建一个 object 时,它将有一个 class ServiceUnAvailable$MockitoMock ,而不是ServiceUnAvailable.class ,这意味着你的下一个检查填充失败:

Predicate<? super Throwable> ClassToControl= throwable -> Exception.contain(throwable.getClass());

Because Exceptions list doesn't contain element ServiceUnAvailable$MockitoMock .因为Exceptions列表不包含元素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: @VolodyaLombrozo 正确地确定了问题的根本原因:

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:让我们重构 CustomPredicate:

  • test() converts wraps input into 1-element list, converts it to a stream, and runs a test with anyMatch. test() 将 wraps 输入转换为 1 元素列表,将其转换为 stream,并使用 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:您有 2 个选项,具体取决于您是否希望谓词传递给子类:

  • pass actual instance of ServiceUnavailable to test() (use new instead of mocking)将 ServiceUnavailable 的实际实例传递给 test()(使用 new 而不是模拟)
  • use instanceof check in test (using stream and anyMatch, but on Exceptions list)在测试中使用 instanceof 检查(使用 stream 和 anyMatch,但在例外列表中)

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:如果你想用一个 class 来测试你的 throwable,一个 lambda 就足够了:

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:如果你使用mockito-inline ,它会改变模拟的构造方式:

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.这个替代模拟制造商使用 Java 工具 API 和子类的组合,而不是创建一个新的 class 来表示模拟。

InlineByteBuddyMockMaker javadoc says: InlineByteBuddyMockMaker javadoc说:

This mock maker will make a best effort to avoid subclass creation when creating a mock.这个 mock maker 将尽最大努力在创建 mock 时避免创建子类。 Otherwise it will use the org.mockito.internal.creation.bytebuddy.SubclassByteBuddyMockMaker to create the mock class. That means that the following condition is true否则它将使用org.mockito.internal.creation.bytebuddy.SubclassByteBuddyMockMaker来创建模拟 class。这意味着以下条件为真

class 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.除非满足以下任何条件,在这种情况下,mock maker 会回退到创建子类。

  • the type to mock is an abstract class.模拟的类型是一个抽象的 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:因此,如果您使用 mockito-inline 测试通过,因为没有创建子类:

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.所以问题是我在 pom.xml 中没有 mockito-inline jar。 Not sure why but this solved the issue不知道为什么,但这解决了问题

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM