在JUnit测试中重复代码

时间:2012-02-20 09:47:21

标签: java unit-testing java-ee junit

在测试类中复制代码是“糟糕的”吗?如你所见,我将驾驶记录添加到驾驶记录中,以便在多种方法中进行测试。将它提取到私有帮助器方法是否更好?或者更清楚地保持它是否像它一样?在这种情况下你会怎么做?

@Test
public void shouldRemoveAllDrivingRecords() {
    Duration duration1 = new Duration(1, 30, 45);
    Duration duration2 = new Duration(2, 50, 12);

    DrivingRecord drivingRecord1 = new DrivingRecord(230.0, duration1, "This was a long trip");
    DrivingRecord drivingRecord2 = new DrivingRecord(300.0, duration2, "This trip is even longer.");

    drivingLog.addDrivingRecord(drivingRecord1);
    drivingLog.addDrivingRecord(drivingRecord2);

    drivingLog.removeAllDrivingLogs();

    assertEquals(0, drivingLog.numberOfDrivingRecords());
}

@Test
public void shouldSumTheDistanceDriven() {
    Duration duration1 = new Duration(1, 30, 45);
    Duration duration2 = new Duration(2, 50, 12);

    DrivingRecord drivingRecord1 = new DrivingRecord(230.0, duration1, "This was a long trip");
    DrivingRecord drivingRecord2 = new DrivingRecord(300.0, duration2, "This trip is even longer.");

    drivingLog.addDrivingRecord(drivingRecord1);
    drivingLog.addDrivingRecord(drivingRecord2);

    double expectedDistanceDriven = drivingRecord1.getDistance() + drivingRecord2.getDistance();
    double totalDistanceDriven = drivingLog.getDistanceDriven();

    assertEquals(expectedDistanceDriven, totalDistanceDriven, 0.1);
}

9 个答案:

答案 0 :(得分:7)

在任何地方复制代码都是“坏事”。此代码是否因某种原因而重复,或仅仅是为了方便。如果有一个原因,你想要在两者中使用相同的数据并以相同的方式添加它,那么一个小的“设置”方法是有意义的。

答案 1 :(得分:7)

我不敢同意大多数答案,对我来说测试代码与生产代码相同。当然,测试代码应该像生产代码一样受到关注和关注,因为它是整体开发工作的一部分,但它们的性质不同。我不会重复自己,而是指出我的另一个答案:Is it OK to copy past unit test when logic is the same

那就是说,重复 测试数据的创建是不好的。更好的方法是考虑为测试而精心创建的数据集,并支持测试大多数情况。可以使用setUp方法创建此数据集。如有必要,可以有多个测试数据集,涵盖业务规则的变体。创建有用的测试数据集并不容易,但值得花一些时间。此外,可以从JSON等加载测试数据。在某些情况下,它更容易维护。

单元测试通常不应相互依赖。但经常,测试用例确实相互依赖。例如,要测试列表,可以测试add()是否有效,isEmpty()是否有效,remove()是否有效。测试remove()假设add()有效。对于这种情况,您可以使用JExample,一个单元测试框架,您可以使用它来“链接”测试。

答案 2 :(得分:6)

您可以使用@org.junit.Before注释方法并初始化该方法中的变量:

public class DrivingLogTest { 
     //suposing DrivingLog class...
     private DrivingLog drivingLog;

     private Duration duration1;
     private Duration duration2;

     private DrivingRecord drivingRecord1;
     private DrivingRecord drivingRecord2;


    @Before 
    public void setUp() { 
       drivingLog=new DrivingLog();
       duration1 = new Duration(1, 30, 45);
       duration2 = new Duration(2, 50, 12);

       drivingRecord1 = new DrivingRecord(230.0, duration1, "This was a long trip");
       drivingRecord2 = new DrivingRecord(300.0, duration2, "This trip is even longer.");

       drivingLog.addDrivingRecord(drivingRecord1);
       drivingLog.addDrivingRecord(drivingRecord2);

    }


    @Test
    public void shouldRemoveAllDrivingRecords() {

       drivingLog.removeAllDrivingLogs();

       assertEquals(0, drivingLog.numberOfDrivingRecords());
    }

    @Test
    public void shouldSumTheDistanceDriven() {

       double expectedDistanceDriven = drivingRecord1.getDistance() +  drivingRecord2.getDistance();
       double totalDistanceDriven = drivingLog.getDistanceDriven();

       assertEquals(expectedDistanceDriven, totalDistanceDriven, 0.1);
    }
}

答案 3 :(得分:2)

单元测试代码是生产代码,因此如上面的答案中所述,应避免所有代码重复。

我通常要么使用@Before方法进行所有变量初始化,要么使用可以在多个单元测试/项目之间共享的TestUtility类。

答案 4 :(得分:1)

代码重复总是很糟糕,没有问号。 为了避免这种情况,请编写可重用的测试场景(实现场景并接受参数的私有方法),然后使用带有特定参数的@Test注释注释的方法调用它。

@Test
public void shouldRemoveAllDrivingRecords() {
    theTest(0);
} 

@Test
public void shouldSumTheDistanceDriven() {
    theTest(0.1);
} 

如果需要,您还可以创建TestSuites。这允许您使用不同的参数运行测试组。

答案 5 :(得分:1)

我认为我们应该始终将它提取到setUp()方法,如果该类中的所有测试方法都需要该代码,或者其他方式是私有帮助方法。

答案 6 :(得分:1)

另见Meszaros'XUnit Test Patterns,其中包含测试代码重复的部分。

@Before是给定片段的方法。

答案 7 :(得分:0)

我也在JUnit中关注DRY原则!我甚至是编写助手或构建器类来执行重复任务。一个好的Junit方法应该首先只包含数据,因此很明显测试什么没有污染如何它被测试。

答案 8 :(得分:0)

将所有常见配置提取到@before。你也可以使用参数,@ Parametrized或我的项目:zohhak,例如

@Before
public void prepareBank() {
  ...
}

@TestWith({
   "25 USD",
   "38 GBP",
   "null"
})
public void testMethod(Money money) {
   ...
}