2011-02-17 68 views
9

我有一个DTO,我从请求对象填充,并且请求对象有很多字段。我想写一个测试来检查populateDTO()方法是否将值放在正确的位置。如果我遵循每个测试的一个断言规则,我将不得不编写大量测试来测试每个字段。另一种方法是在单个测试中写入多个断言。是否真的建议遵循每个测试规则的一个断言,或者我们能否放松这些情况。我如何处理这个问题?如何避免JUnit测试中的多重声明?

回答

0

该规则是否扩展为循环?考虑这个

Collection expectedValues = // populate expected values 
populateDTO(); 
for(DTO dto : myDtoContainer) 
    assert_equal(dto, expectedValues.get(someIndexRelatedToDto)) 

现在我不是那么大的确切的语法,但这只是我正在看的概念。

编辑: 的意见后...

答案是... 都能跟得上!

原理存在的原因是,您可以识别对象的哪些部分失败。如果你用一种方法拥有它们,你将只碰到一个断言,然后是下一个,然后是下一个,你不会看到它们全部。

所以,你可以有两种方式这一个:

  1. 一种方法,较少的样板代码。
  2. 许多方法,在试运行

这是给你更好的报告,都有起伏。 3.列表项目

+0

DTO是POJO ..并且请求也是POJO。所以assertes很可能看起来像assertEqual(“abc”,dto.getName());等。 – Ravi 2011-02-17 05:43:52

+0

所以你想避免只是`assert(dto.equals(mockObject)`,那是你在说什么?你想断言每个单独的元素,而不违反每个原则? – corsiKa 2011-02-17 05:46:18

+0

是的,我想断言每个单独的元素,而不违反每个原则。只是你知道,请求是一个JAXB生成的对象。 – Ravi 2011-02-17 06:00:06

1

您可以有一个parameterized test其中第一个参数是属性名,第二个参数是期望值。

-1

或者你可以做一些解决方法。

import junit.framework.Assert; 
import org.junit.After; 
import org.junit.AfterClass; 
import org.junit.Before; 
import org.junit.BeforeClass; 
import org.junit.Test; 

public class NewEmptyJUnitTest { 

    public NewEmptyJUnitTest() { 
    } 

    @BeforeClass 
    public static void setUpClass() throws Exception { 
    } 

    @AfterClass 
    public static void tearDownClass() throws Exception { 
    } 

    @Before 
    public void setUp() { 
    } 

    @After 
    public void tearDown() { 
    } 


    @Test 
    public void checkMultipleValues() { 
     String errMessages = new String(); 

     try{ 
      this.checkProperty1("someActualResult", "someExpectedResult"); 
     } catch (Exception e){ 
      errMessages += e.getMessage(); 
     } 

     try{ 
      this.checkProperty2("someActualResult", "someExpectedResult"); 
     } catch (Exception e){ 
      errMessages += e.getMessage(); 
     } 

     try{ 
      this.checkProperty3("someActualResult", "someExpectedResult"); 
     } catch (Exception e){ 
      errMessages += e.getMessage(); 
     } 

     Assert.assertTrue(errMessages, errMessages.isEmpty()); 


    } 



    private boolean checkProperty1(String propertyValue, String expectedvalue) throws Exception{ 
     if(propertyValue == expectedvalue){ 
      return true; 
     }else { 
      throw new Exception("Property1 has value: " + propertyValue + ", expected: " + expectedvalue); 
     } 
    } 

     private boolean checkProperty2(String propertyValue, String expectedvalue) throws Exception{ 
     if(propertyValue == expectedvalue){ 
      return true; 
     }else { 
      throw new Exception("Property2 has value: " + propertyValue + ", expected: " + expectedvalue); 
     } 
    } 

     private boolean checkProperty3(String propertyValue, String expectedvalue) throws Exception{ 
     if(propertyValue == expectedvalue){ 
      return true; 
     }else { 
      throw new Exception("Property3 has value: " + propertyValue + ", expected: " + expectedvalue); 
     } 
    } 
} 

也许不是最好的办法,如果过度使用,可以混淆......但这是一种可能性。

+0

我会说任何使用过度。所有这些样板只是为了解决“每次测试中的单个断言”? – merryprankster 2012-09-28 12:20:31

7

保持它们分开。单元测试应该告诉你哪个单元失败。使它们保持分离还可以让您快速隔离问题而不需要经过漫长的调试周期。

0

[告诫:我用Java/JUnit的很“unfluent”,所以在下面的详细信息的错误提防]

有几个方法可以做到这一点:

1)写入多个断言在相同的测试中。如果你只测试一次DTO代,这应该没问题。你可以从这里开始,并在开始伤害时转向另一个解决方案。

2)写一个帮手断言,例如, assertDtoFieldsEqual,传入预期的和实际的DTO。在助手声明中,你分别声明每个字段。这至少给你每个测试只有一个断言的错觉,并且如果你测试多个场景的DTO生成,它会让事情变得更加清晰。

3)为检查每个属性并实现toString的对象实现equals,以便至少可以手动检查断言结果以找出哪个部分不正确。

4)对于生成DTO的每个场景,创建一个单独的测试夹具,用于生成DTO并在setUp方法中初始化期望的属性。为测试每个属性创建一个单独的测试。这也会导致很多测试,但它们至少只会是单线测试。例如,在伪代码:

public class WithDtoGeneratedFromXxx : TestFixture 
{ 
    DTO dto = null; 

    public void setUp() 
    { 
    dto = GenerateDtoFromXxx(); 
    expectedProp1 = ""; 
    ... 
    } 

    void testProp1IsGeneratedCorrectly() 
    { 
    assertEqual(expectedProp1, dto.prop1); 
    } 
    ... 
} 

如果你需要测试在不同情况下的DTO的生成和选择这最后的方法,它马上会变成乏味写所有这些测试。如果是这种情况,你可以实现一个抽象的基础设备,省去了关于如何创建DTO和设置派生类的期望属性的细节。伪代码:

abstract class AbstractDtoTest : TestFixture 
{ 
    DTO dto; 
    SomeType expectedProp1; 

    abstract DTO createDto(); 
    abstract SomeType getExpectedProp1(); 

    void setUp() 
    { 
    dto = createDto(); 
    ... 
    } 

    void testProp1IsGeneratedCorrectly() 
    { 
    assertEqual(getExpectedProp1(), dto.prop1); 
    } 
    ... 
} 


class WithDtoGeneratedFromXxx : AbstractDtoTest 
{ 
    DTO createDto() { return GenerateDtoFromXxx(); } 
    abstract SomeType getExpectedProp1() { return new SomeType(); } 
    ... 
} 
4

是否真的有推荐only one assert per unit test?是的,有人提出这个建议。他们是对的吗?我不这么认为。我很难相信这样的人很长一段时间都在真正的代码上工作过。

所以,imangine你有一个你想单元测试的增变器方法。增变器具有某种你想要检查的效果或效果。通常,增变器的预期效果数量很少,因为许多效果表明增变器的设计过于复杂。每一个效果都有一个断言,每个断言一个测试用例,你不需要每个mutator的很多测试用例,所以这个建议看起来并不是那么糟糕。

但是这个推理的缺陷在于那些测试只关注增变器的预期效果。但是如果增变器有一个bug,它可能会有意想不到的副作用。测试做出愚蠢的假设,即代码没有一整套错误,并且未来的重构不会引入此类错误。当最初编写该方法时,作者可能会明白特定的副作用是不可能的,但重构和添加新功能可能会导致此类副作用。

测试长寿代码的唯一安全方法是检查增变器是否没有意想不到的副作用。但你如何测试这些?大多数类有一些不变量:没有增变器可以改变的事情。例如,容器的size方法永远不会返回负值。实际上,每个不变量都是每个增变器(以及构造器)的后置条件。每个增变器通常还具有一组不变量,用于描述如何改变其死亡而不是。例如,一个sort方法不会改变容器的长度。实际上,类和增变不变量是过去的增变器调用的后置条件。为他们添加断言是检查意外副作用的唯一方法。

那么,只需添加更多测试用例?在实践中,不变量的数量乘以要测试的增变器的数量很大,因此每个测试的一个断言会导致很多测试用例。关于你的不变式的信息分散在许多测试用例中。设计更改以调整一个不变量将需要更改许多测试用例。它变得不切实际。它更适合为mutator设置参数化测试用例,它使用几个断言来检查mutator的几个不变量。