java 如何用logger为方法编写模拟测试?

6jygbczu  于 2023-04-04  发布在  Java
关注(0)|答案(1)|浏览(120)

我有以下接口:

public interface SomeProcessor<T> {
    void process(Stream<T> someStream);

    default <R> R someMap(Function<T, R> function,
                             T message,
                             Logger log) {
        try {
            return function.apply(message);
        } catch (Exception exception) {
            log.error("Mapping error for message => {}", message, exception);
            return null;
        }
    }
}

并希望通过模拟测试覆盖方法的日志部分,但不幸的是它总是失败。现在我有以下测试:

@ExtendWith(MockitoExtension.class)
class SomeProcessorTest<T> implements SomeProcessor<T> {

    @Mock
    private Logger mockLogger;

    @Override
    public void process(Stream<T> someStream) {
    }

    @Override
    public <R> R someMap(Function<T, R> function, T message, Logger log) {
        try {
            return function.apply(message);
        } catch (Exception exception) {
            log.error("Mapping error for message => {}", message, exception);
            return null;
        }
    }

    @Test
    void testWithException() {

        SomeProcessor<String> processor = new SomeProcessor <String>() {
            @Override
            public void process(Stream<String> someStream) {
            }

            @Override
            public <R> R someMap(Function<String, R> function, String message, Logger log) {
                throw new RuntimeException("test exception");
            }
        };

        Function<String, Integer> function = Integer::parseInt;
        String message = "123";
        assertThrows(RuntimeException.class, () -> processor.someMap(function, message, mockLogger));

        ArgumentCaptor<String> someCaptor = ArgumentCaptor.forClass(String.class);
        ArgumentCaptor<Throwable> exceptionCaptor = ArgumentCaptor.forClass(Throwable.class);
        verify(mockLogger).error(eq("Mapping error for message => {}"), eq(message), any(RuntimeException.class));
        assertEquals("Mapping error for message => {}", someCaptor.getValue());
        assertTrue(exceptionCaptor.getValue() instanceof RuntimeException);
    }}

但是我总是看到与Wanted but not invoked: mockLogger.error...相关的错误,并且我无法在我的接口中覆盖以下行

catch (Exception exception) {
            log.error("Mapping error for message => {}", message, exception);
            return null;

我应该如何修改我的模拟测试来覆盖这些行并且不捕获错误?请给予我一条建议。

c6ubokkw

c6ubokkw1#

在你的代码中似乎有很多混淆的东西,乍一看,它背后的原理并不清楚。mockLogger.error没有被调用的问题的根源在于你在测试方法中从匿名类的接口重写了someMap方法的默认实现:

@Override
public <R> R someMap(Function<String, R> function, String message, Logger log) {
    throw new RuntimeException("test exception");
}

因此,您尝试测试的代码根本不会被调用,而是应该从匿名类调用super.someMap(...)来调用实际的接口方法默认实现。
但是,当您这样做时,被抛出的RuntimeException的Assert将失败,因为原始方法消耗了异常并返回null-没有抛出异常。
删除抛出异常的Assert仍然不会使测试通过,因为您正在测试的函数(Integer::parseInt)对于您提供的message应该可以正常工作(x 1m 7 n 1x),所以catch块不会被执行,因为没有抛出异常。为了解决这个问题,你应该传递一个会导致解析失败的消息值-而不是一个数字(例如"abc")。
最后-我不明白为什么你的类实现SomeProcessor<T>,但这似乎是多余的,可能会被删除。

相关问题