views:

310

answers:

9

I've begun to use TDD. As mentioned in an earlier question the biggest difficulty is handling interface changes. How do you reduce the impact on your test cases as requirements change?

+1  A: 

You write the tests before you write the code for the new interface.

Paul Whelan
+6  A: 

I think this is one of the reasons for the trendy argument that interfaces are used too much.

However, I disagree.

When requirements change -- so should your tests. Right? I mean, if the criteria for which you've written the test is no longer valid, then you should rewrite or eliminate that test.

I hope this helps, but I think I may have misunderstood your question.

Ian P
A: 

If you are following the Test First approach, there should in theory be no impact of interface changes on your test code. After all, when you need to change an interface, you'd first change the test case(s) to match the requirements and then go ahead and change your interfaces/implementation until the tests pass.

Martin Klinke
+4  A: 

There will be an impact. You just have to accept that changing the interface will require time to change the associated test cases first. There is no way around this.

However then you consider the time you save by not trying to find an elusive bug in this interface later and not fixing that bug during the release week it is totally worth it.

Ilya Kochetov
+4  A: 

Changing an interface requires updating code that uses that interface. Test code isn't any different from non-test code in this respect. It's unavoidable that tests for that interface will need to change.

Often when an interface changes you find that "too many" tests break, i.e. tests for largely unrelated functionality turn out to depend on that interface. That can be a sign that your tests are overly broad and need refactoring. There are many possible ways this can happen, but here's an example that hopefully shows the general idea as well as a particular case.

For instance if the way to construct an Account object has changed, and this requires updating all or most of your tests for your Order class, something is wrong. Most of your Order unit tests probably don't care about how an account is made, so refactor tests like this:

def test_add_item_to_order(self):
    acct = Account('Joe', 'Bloggs')
    shipping_addr = Address('123 Elm St', 'etc' 'etc')
    order = Order(acct, shipping_addr)
    item = OrderItem('Purple Widget')
    order.addItem(item)
    self.assertEquals([item], order.items)

to this:

def make_order(self):
    acct = Account('Joe', 'Bloggs')
    shipping_addr = Address('123 Elm St', 'etc' 'etc')
    return Order(acct, shipping_addr)

def make_order_item(self):
    return OrderItem('Purple Widget')

def test_add_item_to_order(self):
    order = self.make_order()
    item = self.make_order_item()
    order.addItem(item)
    self.assertEquals([item], order.items)

This particular pattern is a Creation Method.

An advantage here is that your test methods for Order are insulated from how Accounts and Addresses are created; if those interfaces change you only have one place to change, rather than every single test that happens to use Accounts and Addresses.

In short: tests are code too, and like all code, sometimes they need refactoring.

spiv
A: 

In TDD, your tests aren't tests. They are executable specifications. IOW: they are an executable encoding of your requirements. Always keep that in mind.

Now, suddenly it becomes obvious: if your requirements change, the tests must change! That's the whole point of TDD!

If you were doing waterfall, you would have to change your specification document. In TDD, you have to do the same, except that your specification isn't written in Word, it's written in xUnit.

Jörg W Mittag
A: 

When interfaces change, you should expect tests to break. If too many tests break, this means that your system is too tightly coupled and too many things depend on that interface. You should expect a few tests to break, but not a lot.

Having tests break is a good thing, any change in your code should break tests.

Hibri
A: 

If requirements change then your tests should be the first thing to change, rather than the interface.

I would start by modifying the interface design in the first appropriate test, updating the interface to pass the newly-breaking test. Once the interface is updated to pass the test, you should see other tests break (as they will be using the outdated interface).

It should be a matter of updating the remaining failing tests with the new interface design to get them passing again.

Updating the interface in a test driven manner will ensure that the changes are actually necessary, and are testable.

+1  A: 

"What we should do to prevents our code and tests from requiments dependency? Seems that nothing. Every time when requiments changed we must change our code & tests. But maybe we can simplify our work? Yes, we can. And the key principle is: incapsulation of code that might be changed."

http://dmitry-nikolaev.blogspot.com/2009/05/atch-your-changes.html

blackliteon