java 使用模拟异常测试自定义 predicate 失败,因为模拟类不正确

kzipqqlq  于 2023-01-29  发布在  Java
关注(0)|答案(3)|浏览(196)

我在下面创建了一个自定义Predicate,并希望使用mockito对其进行测试。我正在创建特定异常类的模拟,因为这些类没有公共构造函数。运行测试后,Assert失败,因为predicate返回false而不是true。在打印模拟异常的class时,它具有WebClientResponseException$ServiceUnavailable$MockitoMock$54675。看起来模拟没有被正确识别。我在这里做错了什么吗?

同品种器械测试

@ExtendsWith(MockitoExtention.class)
class PredicateTest{

@InjectMocks
CustomPredicate customPredicate;


@Test
public void testPredicate(){

final ServiceUnavailable serviceUnavailable = mock(ServiceUnAvailable.class);

assertTrue(customPredicate.test(serviceUnavailable))
    
}  
}

自定义等同

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);

}

}
eqqqjvef

eqqqjvef1#

实际上问题出在mock(ServiceUnAvailable.class)--当你用这种方法创建一个对象时,它会有一个类ServiceUnAvailable$MockitoMock,而不是ServiceUnAvailable.class,这意味着你的下一个检查填充失败:

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

因为Exceptions列表不包含元素ServiceUnAvailable$MockitoMock
为了以这种方式测试异常,我建议进行下一个修复(我稍微修改了代码,但我希望思路清晰):
同品种器械:

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);
    }
}

试验:

public class PredicateTest {
    @Test
    public void testPredicate() {
        final IllegalStateException serviceUnavailable = Mockito.mock(IllegalStateException.class);
        CustomPredicate customPredicate = new CustomPredicate(serviceUnavailable.getClass());
        assertTrue(customPredicate.test(serviceUnavailable));
    }
}
jckbn6z7

jckbn6z72#

@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

除了他的回答之外,我还想建议更多更改代码的选项:

让我们重构自定义 predicate :

  • test()将输入转换成一个单元素列表,将其转换成一个流,然后用anyMatch运行测试。这是令人困惑的,也是不必要的。
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());
    }
}

修复测试

您有两个选项,具体取决于您是否希望 predicate 传递子类:

  • 将ServiceUnavailable的实际示例传递给test()(使用new而不是mock)
  • 在测试中使用instanceof签入(使用stream和anyMatch,但位于异常列表中)

所有这些都假设这是伪代码,并且你的Exceptions列表比较长。如果你想用一个类来测试你的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

更新:模拟内联

如果使用mockito-inline,则会更改构造模拟的方式:
这个替代的mock maker使用Java工具API和子类化的组合,而不是创建一个新的类来表示mock。
InlineByteBuddyMockMaker的javadoc说:
这个mock maker在创建mock时会尽量避免创建子类,否则会使用org.mockito.internal.creation.bytebuddy.SubclassByteBuddyMockMaker来创建mock类,这意味着下面的条件成立

class Foo { }
assert mock(Foo.class).getClass() == Foo.class;

除非满足以下条件中的任何一个,否则在这种情况下,模型制作者退回到子类的创建。

  • 要模拟的类型是抽象类。
  • 模拟被设置为需要额外的接口。
  • mock被显式设置为支持序列化

因此,如果您使用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
tyg4sfes

tyg4sfes3#

所以问题是我没有mockito内联jar在pom中。不知道为什么,但这解决了问题

相关问题