背景
最近写代码,遇到一个问题。一个函数有许多分支当满足一定条件时,直接退出,并且没有返回值。
if (sStatusChanged)) {
log.info("no need to calculate for user id : {}", userId);
return;
}
如果想验证函数在正确的位置退出了,我能想到的方法,一种方法是验证 log.info 函数被正确执行了,另外一种方法是验证下面的逻辑没有执行(比如 userDao.getUser(userId); 没有执行。由于 userDao 往往是 mock 的,比较容易验证有没有执行)。第一种方法是比较准确的。第二种方法由于 userDao.getUser(userId); 可能还有一个判断分支直接退出,不完全准备。
我在使用第一种方法做测试时,遇到了一些困难,做一下记录。
@Log4j2 中的 log 变量到底是从哪里来的
添加 @Log4j2 跟在代码里添加 private static final Logger log = LogManager.getLogger(MyClass.class); 效果其实是一样的,在编译的阶段,@Log4j2 会帮你生成一个 log 变量供你使用。
初始化测试类之前 mock static
这是今天最主要的犯错。
- 跟 MockitoAnnotations.openMocks(this) 没关系。
- 跟 @ExtendWith(MockitoExtension.class) 也没关系。
- 跟 src/main/resource/log4j2.xml 里的内容也没关系。
appConfig.when(() -> LogManager.getLogger(CaseSlaHandle2.class)).thenAnswer(invocation -> {
System.out.println("LogManager.getLogger called");
return mockLogger;
});
userSlaHandler2 = new UserSlaHandle2(); 测试类的初始化一定要在 mock static 之后,否则 mock 不起作用。
发现另外一个问题是,由于 log 是静态变量,类一直在使用同一个 log 对象(即使重新 new 之后)。
解决方法是:
-
使用 private static Logger logger = mock(Logger.class); ,不使用 @Mock。注意,logger 是静态的,这样 logger 就固定住了。本质原因是,在 JUnit 测试中使用 @Mock 注解来模拟对象时,每次运行测试方法时都会创建一个新的 mock 实例。
-
在方法执行之前和执行之后,运行 Mockito.reset(logger);。