对Polystat的CalcTest.java真实单元测试的简化

153 阅读3分钟

记录是调试的一个不可避免的部分。至少在现代高级编程语言和架构中是这样。三十年前,在汇编中不是这样,但现在是这样。有时我们会追踪变量,但很少。更多时候,我们只是把它们打印到控制台。此外,我们不只是使用println 或任何用于控制台打印的东西来打印它们;相反,我们将消息发送到一个日志框架,它处理控制台或任何其他日志目的地,如文件。这种框架的好处是,我们不需要在调试结束后删除日志--我们只需要配置框架,在生产环境中抑制所有调试级别的消息。一些日志可能发生在单元测试中。我们是否也要把它们留在那里,或者不要?

这里有一个例子(它是对PolystatCalcTest.java的真实单元测试的简化,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,就不会有控制台输出,这意味着对未来的开发者来说没有噪音。

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

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

我想说,单元测试中的每一行日志都是作者发出的信息:"我对现在看到的数据有所了解,但我懒得告诉你,你只要相信我,它看起来不错"。