2016-09-21 34 views
4

在用Java编写的大中型开源项目中,我们收到许多错误报告,针对这些错误报告的单元测试更少,以及更少的关闭这些错误的补丁。了解@ Ignore'd测试何时传递

当提供单元测试但未提供补丁时,我们通过将测试功能添加到套件来验证是否存在针对干线的缺陷。我们承诺它,但添加一个注释,以免打破已知bug的构建。

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

比方说,我们有一些越野车功能 - 引发异常,有意外的副作用,或返回错误的值。 (Real world example

public void buggyFunction() { 
    throw new UnsupportedOperationException("this will be implemented later"); 
} 

而一些单元测试,以测试buggyFunction

@Test 
public void knownIssue() { 
    buggyFunction(); 
} 

选项1@Ignore测试

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

选项2:标准junit4方式是使用@Test(expected=MyException.class)或撒上@RuleExpectedException s整个测试功能。也不要给用户一个有用的信息,说明为什么一个失败的测试意味着一个错误已经修复,并且更新单元测试并关闭错误。另外,如果抛出期望的异常,则测试通过,但在这种情况下测试没有意义。如果失败(错误修复时)或跳过(当错误仍然打开时)会更好。

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

OR

@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()); 
} 

选项3:布格了锅炉板脚手架测试

@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!"); 
} 

是否有没有更好的方法是:

  • 做当已知的问题是开放
  • 通知我们,当一个已知的问题是
  • 优选的是,作为已知的问题是开放的,只要算作一个跳过测试
  • 最好,乱the-不破坏构建从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()) 

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

这个装饰器是100%可重用的,所以没有复制面食尝试/除了脚手架,我可以删除一行代码,当已知问题已修复时,最重要的是我可以离开测试案例逻辑。

任何想法,如果这可以被翻译成Java 6或7?

+0

以我的经验,最好的方法是不要放弃忽略,不要指望Exception,不要添加try catch,而是要解决实际问题。 – Stultuske

+2

@Stultuske这是一个很好的主意,显然每个人都会同意。但在现实世界中,特别是在大型组织和大型项目中,事情并不那么简单。或者你真的认为他会写这个冗长的问题,如果他会谈论他可以在此期间修复的两个错误?! – GhostCat

+0

@GhostCat你的意思是你认为将产品投入生产是可以接受的,因为知道其中存在缺陷,因为你有可能忽略测试吗? – Stultuske

回答

2

除了您自己勾画的选项之外,我没有看到其他技术选项。

我认为解决方案来自不同的角度:你的工具不给开发人员他们需要的反馈。你看,重点是:当开发者决定修复buggyFunction()那么这需要严格组织的方式发生。含义:

开发商想要修复的一个错误。因此,他应该充分认识到错误的,和所有涉及到这项任务的工作:

  • 在你的bug跟踪系统进行更新
  • 更重要的是:运行所有测试!

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

所以即使他偶然修复了这个bug,关键是他通知了他的变化在别处打破了考验。然后,他的责任是什么,其他测试打破了。如果你的系统不能有效地支持这个工作流程,那么改变你的单元测试根本无济于事。因为你的单元测试是而不是这里真正的问题。

从这个角度来看,唯一的选择是在选项2和3之间。因为它们给你“最好”的系统这部分可以给你的东西:有人更改代码,然后测试中断。尽快通知更改代码的人;然后确定发生了什么。从那里开始,这只关乎测试的质量(指出测试失败时意味着什么);和所涉人员的技能(然后做正确的事情)。

3

创建一个单独的TestSuite错误识别器,只有当错误出现(!)时才会通过。

/** 
* @see ...original now ignored test 
*/ 
@Test 
public void defect123124() { 
    expectThrows(UnsupportedOperationException.class,() -> buggyFunction()); 
} 

当测试“失败”时,表示您可以删除它并取消缺陷。 当然,常规测试套件中需要进行反向测试。有@Ignore删除。

现在你有一个很好的概述。另外,程序员修复了一些不同的东西,并且突然失败的错误测试套件获得了他的奖金。

1

如果您正在使用Testsuites或使用它们是您的一个选项,您可以尝试使用JUnit Category注释。 (或者取决于你的构建系统,你甚至可以在没有测试套件的情况下做到。查找链接文章的更多细节。)

因此,你需要创建一个标记接口:

public interface KnowIssue { 
    // JUnit category marker interface 
} 

代替将@Ignore注释您应用类别发生故障的测试方法(甚至是整个测试类)和:

@Test 
@Category(KnowIssue.class) 
public void myFailingTest() { 
    // some test 
} 

然后在套件(S)为您的绿色测试排除了KnownIssue类别:

@RunWith(Categories.class) 
@ExcludeCategory(KnownIssue.class) // <- exclude known issues! 
@Suite.SuiteClasses({ /** your Testclasses here */ }) 
public class AllMyGoodTests { 
    // test suite 
} 

要检查你的已知问题创建第二个测试套件,你欠幅帽子(regularily),看看你的任何已知问题得到固定的(绿色)的事件:

@RunWith(Categories.class) 
@IncludeCategory(KnownIssue.class) // <- only run known issues! 
@Suite.SuiteClasses({ /** your Testclasses here */ }) 
public class KnownIssueTests { 
    // test suite 
} 

然后移除该测试的类别,它会再次自动执行您的“良好”测试。

甚至可能用自定义test runner@RunWith(KnownIssueCategoryRunner.class)反转测试结果(红色< - >绿色),但我从来没有真正尝试过。