使用Mockito / PowerMock验证已写入日志

时间:2013-07-10 08:44:48

标签: java logging junit mockito powermock

我有一个将消息写入某些日志的类。该类是一个实用程序,它不执行任何其他操作,它在后台运行,检查一些内容并记录它们。我想知道我是否有可能在单元测试中验证日志已写入,而不关心实际写入的内容。这是我的班级正在测试中:

//imports...

public class MyClass {
  private static Log log = LogFactory.getLog(MyClass.class);

  public MyClass() {
    log.info("MyClass is being created.");
  }

  public void doThing() {
    if( everything_is_fine ) {
      log.info("This is a message to say everything is fine.");
    } else {
      log.error("Uh oh...");
    }
  }
}

我的测试员班:

// imports ...

@RunWith(PowerMockRunner.class)
@PrepareForTest({MyClass.class,LogFactory.class})
public class MyClassTest {
  Log mockLog;

  @Before
  public void setup() {
    PowerMockito.mockStatic(LogFactory.class);
    mockLog = mock(Log.class);
    PowerMockito.when(LogFactory.getLog(MyClass.class)).thenReturn(mockLog);
  }

  @Test
  public void test_everything_is_ok() {
    MyClass mything = new MyClass();    // should write to log.info
    mything.doThing();                  // should write to log.info

    verify(mockLog, atLeastOnce()).info(anyString());
    verify(mockLog, never()).error(anyString());
  }

  @Test
  public void test_everything_is_not_ok() {
    MyClass mything = new MyClass();    // should write to log.info

    // do something which makes things not ok

    mything.doThing();                  // should write to log.error

    verify(mockLog, atLeastOnce()).info(anyString());
    verify(mockLog, atLeastOnce()).error(anyString());
  }
}

当我运行测试时,我希望为两个测试调用log.info(),并且仅针对第二个调用log.error()。但是我为两个测试的log.info得到了“通缉但未调用”。对于第二个的log.error。所以要么:
1)我的代码被破坏而没有写入日志,或者 2)我的考试被打破了。

我认为我在测试中搞砸了一些东西,可能是非常明显的东西,所以有没有人有经验测试这样可以帮助我的东西?任何和所有帮助将不胜感激。

更新

感谢那些帮助过的人,我现在已经找到了解决方案。

在玩了一下代码后,我发现日志的初始化似乎存在问题。做private static Log log = LogFactory.getLog(MyClass.class);似乎没有正确使用模拟,所以如果我将它移动到构造函数,它似乎被模拟好了,我的测试都按预期工作:

public class MyClass {
  private static Log log;

  public MyClass() {
    MyClass.log = LogFactory.getLog(MyClass.class);
    log.info("MyClass is being created.");
  }

  // etc ...
}

我现在已经开始工作,但任何人都可以解释为什么第一种方式初始化日志不起作用?或者也许把我指向可以解释它的地方?我不确定我对Java初始化对象的理解是否存在差距,或者它是否是模拟框架的限制。

2 个答案:

答案 0 :(得分:0)

您可以像我们在项目中所做的那样对Logger进行抽象:

https://github.com/4finance/uptodate-gradle-plugin/blob/master/src/main/groovy/com/ofg/uptodate/LoggerProxy.groovy

然后你必须建构者

https://github.com/4finance/uptodate-gradle-plugin/blob/master/src/main/groovy/com/ofg/uptodate/UptodatePlugin.groovy

一个用LoggerProxy初始化,一个用于测试,你可以在其中模拟它并验证是否传递了正确的文本。不需要肮脏的黑客;)

答案 1 :(得分:0)

由于我遇到了同样的问题(private static Log log = LogFactory.getLog(MyClass.class);),我发布了解决问题的方法。

我没有修改记录器初始化(我受到编码规则的限制),而只是将测试分成不同的类......