4

Is it "bad" to duplicate code in the testclass? As you see I add drivingrecords to the drivinglog to have something to test on in multiple methods. Is it better to extract this to a private helper method or is it better for clarity to keep it like it is? What do you do in such cases?

@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);
}
ewernli
  • 38,045
  • 5
  • 92
  • 123
LuckyLuke
  • 47,771
  • 85
  • 270
  • 434

9 Answers9

9

You can annotate a method with @org.junit.Before and initialize the variables in that method:

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);
    }
}
Tomas Narros
  • 13,390
  • 2
  • 40
  • 56
9

I dare to disagree with most answers, and to me test code is not the same as production code. Sure, test code deserves the same care and attention as production code, because it's part of the overall development effort, but they differ in nature. I will not repeat myself, and rather point to another answer of mine: Is it OK to copy past unit test when logic is the same.

That said, repeating the creation of test data is bad. What is better is to think of a data set that is carefully created for testing, and support testing the most cases. This data set can be created in the setUp method. There can be several test data sets, covering variations of the business rules, if necessary. Creating useful test data sets is not easy, but it's worth spending some time on it. Also, test data could be loaded from JSON or so. In some case, it's easier to maintain.

Unit tests should normally not depend on each other. But frequently, test case do depend on each other. For instance, to test a list, one want to test that add() works, that isEmpty() works, that remove() works. Testing remove() assumes that add() works. For such scenario, you can use JExample, a unit testing framework with which you can "chain" tests.

Community
  • 1
  • 1
ewernli
  • 38,045
  • 5
  • 92
  • 123
7

It's "bad" to duplicate code anywhere. Is this code duplicated for a reason or was it simply convenience. If there's a reason yoy want to use the same data in both and add it the same way, then a wee "setup" method makes sense.

Tony Hopkinson
  • 20,172
  • 3
  • 31
  • 39
2

Unit test code is production code, so as stated in the answers above, all code duplication is to be avoided.

What I usually do is either use the @Before method for all variable initialization or use a TestUtility class that can be shared between multiple unit tests / projects.

GETah
  • 20,922
  • 7
  • 61
  • 103
1

Code duplication is always bad with no question sign. To avoid it in your case write reusable test scenario (private method that implements the scenario and accepts arguments) and then call it from methods annotated with @Test annotation with specific arguments.

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

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

You can also create TestSuites if you need. This allows you to run groups of tests with different parameters.

AlexR
  • 114,158
  • 16
  • 130
  • 208
1

I think we should always extract it to either setUp() method if that code is required for all test methods in that class or to a private helper method otherwise.

Kuldeep Jain
  • 8,409
  • 8
  • 48
  • 73
1

See also Meszaros' XUnit Test Patterns which includes a section on test code duplication.

@Before is the way to go for the given fragment.

avandeursen
  • 8,458
  • 3
  • 41
  • 51
0

extract all common configuration to @before. you can also use parameters, with @Parametrized or with mine project: zohhak, eg.

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

@TestWith({
   "25 USD",
   "38 GBP",
   "null"
})
public void testMethod(Money money) {
   ...
}
piotrek
  • 13,982
  • 13
  • 79
  • 165
0

I follow the DRY prinicple also in JUnit! I even writer helper or builder classes to do repeating tasks. A good Junit method should contain at first glance only the data so it is apparent what is tested without pollution how it is tested.

Strinder
  • 2,111
  • 2
  • 21
  • 34