2011-06-15 61 views
5

我在思考异常处理和单元测试的最佳实践,因为我们试图获得一些代码最佳实践。JUnit4 @Test(expected = MyException.class)VS try/catch

上一篇关于最佳实践的文章,在我们的公司wiki上发现,声明“不要使用try/catch,而是使用Junit4 @Test(expect = MyException.class)”,而没有进一步的信息。我不相信。

我们的许多自定义异常都有一个枚举,以便找出故障原因。 结果,我宁愿看到这样一个试验:

@Test 
public void testDoSomethingFailsBecauseZzz() { 
try{ 
    doSomething(); 
} catch(OurCustomException e){ 
    assertEquals("Omg it failed, but not like we planned", FailureEnum.ZZZ, e.getFailure()); 
} 
} 

比:

@Test(expected = OurCustomException.class) 
public void testDoSomethingFailsBecauseZzz() { 
    doSomething(); 
} 

时doSomethig()看起来像:

public void doSomething throws OurCustomException { 
    if(Aaa) { 
    throw OurCustomException(FailureEnum.AAA); 
    } 
    if(Zzz) { 
    throw OurCustomException(FailureEnum.ZZZ); 
    } 
    // ... 
} 

在一个侧面说明,我不仅如此,在某些情况下,@Test(expected = blabla.class)是最好的选择(例如,当异常是确切的,并且可以确定是什么引起的)。

我在这里丢失了什么东西,或者我应该在必要时使用try/catch?

回答

5
  • 如果您只是想检查是否抛出了某种类型的异常,请使用注释的expected属性。
  • 如果您想检查抛出的异常的属性(例如消息或自定义成员值),请在测试中捕获它并进行断言。

在你的情况,似乎你想要后者(声称异常有一定的FailureEnum值); 使用try/catch没有任何问题。

你应该“不使用try/catch”(解释为“从不”)的概括是双层的。

杰夫是对的;您的例外层次结构的组织是可疑的。但是,你似乎认识到这一点。 :)

+0

事实上,当我看到你们说异常层级被怀疑时,我很开心,它已经困扰了我一段时间。 (IT可能就是为什么我在思考这些问题;))。谢谢! – Stph 2011-06-15 15:18:21

6

这听起来像你的枚举被用来作为一个异常层次的替代?也许如果你有一个异常层次,@Test(expected=XYZ.class)会变得更有用?

+1

是的,他们真的很喜欢枚举和字符串属性,而不是一个真正的面向对象的代码;)这可能是在我们的一些异常处理代码中的所有邪恶的根源之一;) – Stph 2011-06-15 14:52:56

+2

如果您在思考最佳实践,为什么不考虑重新分解异常层次结构?使其在未来更加有用和可维护? – 2011-06-15 14:55:52

+0

对于整个应用程序的嗯重构是可悲的,现在是不可能的。最终它会被需要,但我没有那种力量或时间;)但是,如果我必须从头开始一个项目或者帮助重构,我肯定会推动使用正确的异常层次结构:) – Stph 2011-06-15 15:07:22

1

在你的特殊情况下,你想测试(1)如果预期的异常被抛出和(2)如果错误号码是正确的,因为该方法可以抛出不同类型相同的异常。

这需要检查异常对象。但是,你能坚持到推荐验证正确的异常被抛出:

@Test(expected = OurCustomException.class) 
public void testDoSomethingFailsBecauseZzz() { 
    try { 
     doSomething(); 
    } catch (OurCustomException e) { 
     if (e.getFailureEnum.equals(FailureEnum.ZZZ)) // use *your* method here 
     throw e; 

     fail("Catched OurCostomException with unexpected failure number: " 
     + e.getFailureEnum().getValue()); // again: your enum method here 
    } 
} 

这种模式将 意想不到的异常,使测试失败。

编辑

改变了它,因为我错过了很明显的:我们可以做一个测试案例失败,并捕获消息。所以现在:测试通过,如果预期的异常预期的错误代码被抛出。如果测试因为出现意外错误而失败,那么我们可以读取错误代码。

+0

难道我们不想避免意外的例外吗?此外,我不想坚持推荐(但如果我必须,我喜欢你的解决方案);) – Stph 2011-06-15 15:15:26

+0

@Stph - 感谢您的评论,我改进了我的答案。只是忘了,我们可以让它失败,并带有失败信息;) – 2011-06-16 06:24:37

2

如果你想检查原始异常类型,那么expected方法是适当的。否则,如果你需要测试一些关于异常的东西(并且不管测试消息内容的怪异原因是什么),你可以尝试一下,但这有点古怪。新的JUnit方法是使用MethodRule。 API(ExpectedException)中的一个专门用于测试消息,但您可以轻松查看代码并调整该实现以检查失败enum s。

+0

嗯。 MethodRule,我从来没有听说过。这是我错过的东西;)谢谢! – Stph 2011-06-15 15:23:45

0

我制作了catch-exception因为我面临着和你一样的问题,Stph。 随着捕获的异常的代码看起来是这样的:

@Test 
public void testDoSomethingFailsBecauseZzz() { 
    verifyException(myObj, OurCustomException.class).doSomething(); 
    assertEquals("Omg it failed, but not like we planned", FailureEnum.ZZZ,  
       ((OurCustomException)caughtException()).getFailure() ; 
} 
1

我碰到这个来寻找如何处理异常的时候。

正如@Yishai所说,预期异常的首选方法是使用JUnit规则和ExpectedException

当使用@Test(expected=SomeException.class)时,如果方法中的任何地方抛出异常,测试方法将通过。

当您使用ExpectedException

@Test 
public void testException() 
{ 
    // If SomeException is thrown here, the test will fail. 
    expectedException.expect(SomeException.class); 
    // If SomeException is thrown here, the test will pass. 
} 

还可以测试:

  • 预期的消息:ExpectedException.expectMessage();
  • 预期原因:expectedException.expectCause()

作为一个方面说明:我不认为使用枚举作为异常消息/原因是好的做法。 (请纠正我,如果我错了。)