英文:
How to write mock test for method with logger?
问题
I have the following interface:
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;
}
}
}
And want to cover log part of the method by mock test but unfortunately it always fails. Now I have the following test:
@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);
}
}
But I always see errors related to Wanted but not invoked: mockLogger.error...
and I cannot cover the following lines in my interface
catch (Exception exception) {
log.error("Mapping error for message => {}", message, exception);
return null;
What should I change in my mock test to cover these lines and not catch an error? Give me a piece of advice, please.
英文:
I have the following interface:
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;
}
}
}
And want to cover log part of the method by mock test but unfortunately it always fails.
Now I have the following test:
@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);
}}
But I always see errors related to Wanted but not invoked: mockLogger.error...
and I cannot cover the following lines in my interface
catch (Exception exception) {
log.error("Mapping error for message => {}", message, exception);
return null;
What should I change im my mock test to cover these lines and not catching an error?
Give me a piece of advice please.
答案1
得分: 1
你的代码中似乎有许多混乱的地方,一开始不太清楚其中的原理。mockLogger.error
没有被调用的问题源于你在测试方法内部的匿名类中覆盖了接口的 someMap
方法的默认实现:
@Override
public <R> R someMap(Function<String, R> function, String message, Logger log) {
throw new RuntimeException("test exception");
}
因此,你试图测试的代码根本没有被调用。相反,你应该从匿名类中调用 super.someMap(...)
来调用实际的接口方法默认实现。
然而,当你这样做时,断言会失败,因为原始方法会捕获异常并返回 null
,不会抛出异常。
移除抛出异常的断言仍然不会使测试通过,因为你正在测试的函数(Integer::parseInt
)应该对你提供的 message
("123"
)正常工作,因此不会抛出异常,catch
块也不会执行。为了解决这个问题,你应该传递一个会导致解析失败的消息值,而不是一个数字(例如 "abc"
)。
最后,我不明白为什么你的类要实现 SomeProcessor<T>
,但这似乎是多余的,可以移除。
英文:
There seems to be a lot of mixed up things in your code and the rationale behind it is not clear at first glance. The problem with mockLogger.error
not being called has its source in the fact that you're overriding the default implementation of the someMap
method from the interface within the anonymous class inside your test method:
@Override
public <R> R someMap(Function<String, R> function, String message, Logger log) {
throw new RuntimeException("test exception");
}
Because of that the code you're trying to test is not called at all. Instead, you should call super.someMap(...)
from your anonymous class to invoke the actual interface method default implementation.
When you do that though, the assertion of RuntimeException
being thrown will fail as the original method consumes the exception and returns null
- no exception is thrown.
Removing the assertion of throwing an exception still would not make the test pass, because the function you're testing (Integer::parseInt
) should work fine for the message
you're providing ("123"
), so the catch
block will not be executed as no exception is thrown. To solve that you should pass a message value that would cause parsing failure - not a number (e.g. "abc"
).
Finally - I don't understand why your class implements SomeProcessor<T>
, but that seems to be redundant and may be removed.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论