知道 @Ignored 测试何时通过

Ice*_*dor 5 java junit unit-testing junit4

在用 Java 编写的中型开源项目中,我们收到许多错误报告,但针对这些错误报告的单元测试较少,并且修复这些错误的补丁也较少。

当提供单元测试但未提供补丁时,我们通过将测试功能添加到套件来验证主干是否存在错误。我们提交它,但junit4 @Ignore为其添加注释,以免因已知错误而破坏构建。

某些错误可能是相关的,或者对一个错误所做的更改可能会修复另一个错误。我们想知道以前已知失败的测试用例何时不再失败,以便我们可以通知观察该错误的人员并关闭该错误。

假设我们有一些有缺陷的函数——引发异常、产生意外的副作用或返回错误的值。(现实世界的例子

public void buggyFunction() {
    throw new UnsupportedOperationException("this will be implemented later");
}
Run Code Online (Sandbox Code Playgroud)

和一些单元测试来测试buggyFunction

@Test
public void knownIssue() {
    buggyFunction();
}
Run Code Online (Sandbox Code Playgroud)

选项 1@Ignore测试

@Ignore("this test is currently failing. see bug 12345")
@Test
public void knownIssue() {
    buggyFunction();
}
Run Code Online (Sandbox Code Playgroud)

选项 2 :标准的 junit4 方式是在整个测试函数中使用@Test(expected=MyException.class)或散布s。@Rule ExpectedException既不向用户提供有用的消息,说明为什么失败的测试意味着错误已被修复,也没有更新单元测试并关闭错误。此外,如果抛出预期的异常,则测试通过,但在这种情况下测试毫无意义。最好是失败(当错误已修复时)或跳过(当错误仍然存​​在时)。

// when this bug is fixed, it should not throw an exception
// TODO: delete expected=UnsupportedOperationException.class
@Test(expected=UnsupportedOperationException.class)
public void knownIssue() {
    buggyFunction();
}
Run Code Online (Sandbox Code Playgroud)

或者

@Rule
public final ExpectedException thrown = ExpectedException.none();

@Test
public void knownIssue() {
    thrown.expect(UnsupportedOperationException.class);
    thrown.expectMessage("this will be implemented later");
    buggyFunction();
    thrown.expect(ExpectedException.none());
}
Run Code Online (Sandbox Code Playgroud)

选项 3:使用样板脚手架进行 Booger up 测试

@Test
public void knownIssue() {
    try {
        buggyFunction();
    } catch (UnsupportedOperationException e) {
        // we know that buggyFunction is broken, so skip this test
        assumeTrue("Skipping test. Expected exception: " + e, false);
    }
    // surprise! buggyFunction isn't broken anymore!
    fail("The function is no longer buggy! " +
         "Update the unit test and close bug 12345!");
}
Run Code Online (Sandbox Code Playgroud)

有没有更好的替代方案:

  • 当已知问题存在时,不要破坏构建
  • 当已知问题得到解决时通知我们
  • 最好,只要已知问题尚未解决,就算作跳过的测试
  • 最好是来自 hamcrest 或其他库的开箱即用解决方案

我可以在 Python 中轻松完成类似的事情,其中​​未计算的函数是第一类对象。在 Java 6 中也可以完成同样的操作(是的,这就是我们使用的版本),但可能需要比选项 3 更多的样板文件。请告诉我我错了。

def alertWhenFixed(expected=Exception, bug=12345):
    def decorator(func):
        def func_wrapper(*args, **kwargs):
            try:
                func(*args, **kwargs)
            except Exception as e:
                if isinstance(e, expected):
                    assumeTrue("Skipping test. Expected exception: {}"
                               .format(e), false)
                else:
                    raise e
            fail("The function is no longer buggy! " +
                 "Update the unit test and close bug {}".format(bug))
        return func_wrapper
    return decorator

@alertWhenFixed(expected=UnsupportedOperationException, bug=12345)
def knownIssue():
    buggyFunctionThrowsException()

@alertWhenFixed(expected=AssertionFailed)
def knownIssue():
    assertEquals(42, buggyFunctionReturnsWrongValue())

@alertWhenFixed(expected=AssertionFailed)
def knownIssue():
    buggyFunctionHasWrongSideEffect()
    assertEquals(42, getSideEffect())
Run Code Online (Sandbox Code Playgroud)

该装饰器可以测试引发异常、返回错误值或产生错误副作用的已知问题。

这个装饰器是 100% 可重用的,所以没有复制粘贴的 try/ except 脚手架,当已知问题得到修复时我可以删除一行代码,最重要的是我可以保留测试用例逻辑。

知道这是否可以翻译到 Java 6 或 7 吗?

Gho*_*ica 2

除了您概述的技术之外,我没有看到其他技术选择。

我认为解决方案来自不同的角度:您的工具没有为开发人员提供他们所需的反馈。你看,重点是:当开发人员决定修复buggyFunction ()时,就需要以严格组织的方式进行。意义:

开发人员想要修复一个错误。因此,他应该充分意识到该错误以及与此任务相关的所有工作:

  • 更新您的错误跟踪系统
  • 更重要的是:运行所有测试!

换句话说:您希望您的开发人员收到快速反馈。他应该能够更改buggyFunction,然后,几分钟后,他应该收到关于现在失败的测试用例的通知。

因此,即使他无意中修复了错误,关键的一点是他会被告知他的更改破坏了其他地方的测试。然后,其他测试失败的原因就是他的责任。如果您的系统不以有效的方式支持该工作流程,那么更改单元测试根本没有帮助。因为你的单元测试并不是这里真正的问题。

从这个角度来看,唯一明智的选择是在选项 2 和选项 3 之间。因为它们为您提供了系统的这一部分可以为您提供的“最佳”功能:有人更改了代码,然后测试失败了。并尽快通知更改代码的人;然后弄清楚发生了什么。从那时起,它只是关于该测试的质量(指出测试失败时意味着什么);以及相关人员的技能(然后做正确的事情)。