Java登录单元测试 一种不好的做法

草莓夹饼干 2021-09-07 09:33:15 浏览数 (1392)
反馈

日志记录是调试过程中不可避免的一部分。好吧,至少在现代高级编程语言和架构中是这样。这不是三十年前的事了,而是现在。有时我们跟踪变量,虽然这样做的很少。更多的时候我们只是将它们打印到控制台。此外,我们不只是使用println控制台打印或我们拥有的任何东西来打印它们;相反,我们将消息发送到日志框架,该框架处理控制台或任何其他日志记录目的地,如文件。这种框架的美妙之处在于我们不需要在调试完成后删除日志——我们只需配置框架以抑制生产环境中的所有调试级别的消息。一些日志记录可能发生在单元测试中,我们是否也把它们留下或者不留下?

这是一个示例(它是对​CalcTest.java​ 中来自​Polystat​的真实单元测试的简化,​Polystat​是我们目前正在研究的静态分析器):

import com.jcabi.log.Logger;
import com.jcabi.xml.XML;
import org.hamcrest.MatcherAssert;
import org.hamcrest.Matchers;
import org.junit.jupiter.api.Test;
public final class FooTest {
  @Test
  public void buildsSimpleXml() {
    final XML xml = new Foo().build();
    Logger.debug(this, "This is the XML:\n%s", xml.toString());
    MatcherAssert.assertThat(
      xml,
      Matchers.notNullValue()
    );
  }
}

这是 Java,我将​JUnit5 + Hamcrest​与我自己的日志记录框架​jcabi-log​一起使用,它是​Slf4j​的装饰器,使用​Log4j​打印到控制台。

这里发生了什么?有一个Foo带有方法的类build(),它生成一个 XML 文档(我使用的是​jcabi-xml​库,它是​JDK DOM​的装饰器)。然后,单元测试将 XML 文档的内容打印到控制台并做出一个非常愚蠢的断言:文档不是 NULL。这很愚蠢,因为如果它是 NULL,那么日志语句在​.toString()​调用时就会失败。

我是这段代码的开发者,所以我知道发生了什么:我懒得写一个正确的断言,它会查看 XML 文档并确保里面有正确的元素。我只是将它打印到控制台,目视确认其有效性并称其为一天。如果我有更多的时间,这就是我编写更好的单元测试的方式(我刚刚对 Polystat 测试进行了改进):

import com.jcabi.matchers.XhtmlMatchers;
import org.hamcrest.MatcherAssert;
import org.junit.jupiter.api.Test;
public final class FooTest {
  @Test
  public void buildsSimpleXml() {
    MatcherAssert.assertThat(
      XhtmlMatchers.xhtml(new Foo().build()),
      XhtmlMatchers.hasXPath("//foo")
    );
  }
}

现在,构建了 XML 文档,然后测试其中是否存在​//foo XPath​。只有在断言失败的情况下,才会将文档的内容打印到控制台。如果 XML 具有所需的 XPath,则不会有控制台输出,这对未来的开发人员意味着没有噪音。

此外,现在它是一个单语句测试,这本身就是一种很好的做法。

回顾我测试和记录的经验,我认为记录单元测试是一个坏主意。有时不可避免,因为我们懒惰或根本没有足够的时间,但仍然很糟糕。日志记录帮助我们在视觉上确认输出的正确性,但它从项目中带走了这些知识。那些将在稍后进行测试的人不会知道我们在那里看到了什么。他们会在控制台看到输出,但不知道它是否仍然符合我在撰写本文时的期望。

我会说单元测试中的每个日志记录行都是来自其作者的消息:“我对我现在看到的数据有所了解,但我懒得告诉你,你只需要相信我好的。”

我建议我们不要在我们的代码中留下这样的消息。


0 人点赞