vendredi 6 novembre 2015

What shall I do if the requirement of a method (with lot of tests already) changed?

I have some questions about the TDD(test driven development): a method has already have some tests, and the requirement changes, what should I do?

Say there is a Taximeter, which has a init fare $6 for 2km, and then $0.8 for each extra kilometer, and $0.25 for every waiting minute. I have writen several tests like this:

public class TaximeterTest {
    @Test public void testInitFare() {
        assertEquals(new Taximeter().calcuate(2), 6.0, 0.00001);
    }
    @Test public void test3km() {
        assertEquals(new Taximeter().calcuate(3), 6.8, 0.00001);
    }
    @Test public void test8km() {
        assertEquals(new Taximeter().calcuate(8), 9.2, 0.00001);
    }
    @Test public void test3kmWaiting1Minute() {
        assertEquals(new Taximeter().calcuate(3, 1), 7.05, 0.00001);
    }
    @Test public void test8kmWaiting10Minutes() {
        assertEquals(new Taximeter().calcuate(8, 10), 11.7, 0.00001);
    }
}

Then the requirement changes: the Taximeter should round off the price, so the 3km, it should be $7, and for 8km, it should be $9.

What should I do now? I have 2 options:

1. Modify all affected existing tests

public class TaximeterTest {
    @Test public void testInitFare() {
        assertEquals(new Taximeter().calcuate(2), 6.0, 0.00001);
    }
    @Test public void test3km() {
        assertEquals(new Taximeter().calcuate(3), 7.0 /*rounded*/, 0.00001);
    }
    @Test public void test8km() {
        assertEquals(new Taximeter().calcuate(8), 9.0 /*rounded*/, 0.00001);
    }
    @Test public void test3kmWaiting1Minute() {
        assertEquals(new Taximeter().calcuate(3, 1), 7.0 /*rounded*/, 0.00001);
    }
    @Test public void test8kmWaiting10Minutes() {
        assertEquals(new Taximeter().calcuate(8, 10), 12.0 /*rounded*/, 0.00001);
    }
}

Seems work OK if there are only a few tests, but if there rules are complex enough, maybe there dozens of tests need to be changed.

I also want to know the real fare beforing rounding, I don't feel safe if I only know the rounded price.

So I'm considering the option 2

2. add concepts of internalPrice and finalPrice

The internalPrice is the price before rounding, and the finalPrice is rounded of internalPrice.

First I need to rename the calculate method to internalPrice, and the class name of the test is changing to TaximeterinternalPriceTest, but all the data in the tests are not changed:

public class TaximeterinternalPriceTest {
    @Test public void testInitFare() {
        assertEquals(new Taximeter().internalPrice(2), 6.0, 0.00001);
    }
    @Test public void test3km() {
        assertEquals(new Taximeter().internalPrice(3), 6.8, 0.00001);
    }
    @Test public void test8km() {
        assertEquals(new Taximeter().internalPrice(8), 9.2, 0.00001);
    }
    @Test public void test3kmWaiting1Minute() {
        assertEquals(new Taximeter().internalPrice(3, 1), 7.05, 0.00001);
    }
    @Test public void test8kmWaiting10Minutes() {
        assertEquals(new Taximeter().internalPrice(8, 10), 11.7, 0.00001);
    }
}

Then create new tests for finalPrice:

public class TaximeterFinalPriceTest {
    @Test public void testInitFare() {
        assertEquals(new Taximeter().finalPrice(2), 6.0, 0.00001);
    }
    @Test public void test3kmWaiting1Minute() {
        assertEquals(new Taximeter().finalPrice(3, 1), 7.0 /*rounded*/, 0.00001);
    }
    @Test public void test8kmWaiting10Minutes() {
        assertEquals(new Taximeter().finalPrice(8, 10), 12.0 /*rounded*/, 0.00001);
    }
}

But the problem is the internalPrice is actually only used in finalPrice, but it should be non-private if I want to test it:

double internalPrice()
public double finalPrice()

I'm puzzled now and not sure which option is better, or there is even better options. Any help?

Aucun commentaire:

Enregistrer un commentaire