We're looking to concentrate more on testing behaviour (as opposed to every class/method individually) in all our automated tests, as espoused by various high-profile online discussions recently. I like the idea of thinking about only what goes in and out of certain logical well-defined boundaries between components, but I'm having a hard time imagining how that relates to quite low-level detail of behaviour, that is only a small part of the overall requirement but still important to be correct.
As an example:
We have a component which accepts a customer order and takes some kind of significant action with it, let's say accepts it and starts processing manufacture. The main thrust of the requirement is about accepting and manufacturing, but there are many small things which need to be correct, and which if incorrect should cause an exception, for example that the order date needs to be valid. If we assume that I don't implicitly trust whatever date validation routine is being used, then there are loads of things I need to test there: basic format, leap years, not in the future, etc etc. In "non-behavioural" TDD I would definitely test those at the class/method level, and then never worry in my other tests about whether they'll work. But if I'm only testing behaviour, do I need to take all those success/fail cases and test them on the component boundary, for every situation where I'm interested in the date?
I can see a few options:
- Massively tedious repetition of millions of tests, as on the low-level methods, but higher up.
- As above, but condense "is the date valid" down into a separate method which I reuse in my behaviour tests, so although I do check that the date is valid, it's only one line per test. This is much more likely than the first option...
- Test only broad behaviour higher-up, and do have a low level set of tests just to ensure the validation routines work. This seems sensible, but the importance of behaviour tests not relying on implementation details has been stressed - the actual validation routine being used is an implementation detail, invisible at the component boundary, so even if I test that routine, can I officially then not test it elsewhere as well somehow?
I'm leaning towards the middle option, but I'm quite new to this, and would be interested what you think about it!