8

This is in part a followup to this question.

I'm not sure the best way to ask this, so I'll try a short story to set the scene:

Once upon a time, there was a class ‘A’, which had a unit test class ‘ATests’ responsible for testing its behaviour through the public interface. They lived happily together for a while and then a change happened, and class ‘B’ came along, which as it turned out had a lot in common with class ‘A’, so a base class was introduced.

The public behaviour of the base class is already covered by the tests for class A. So, the question is what happens next?

• Does class B need to have tests for the common (base class behaviour)? It seems like the behaviour is a part of B, so it should be tested, but should these tests be shared with those for class A? For the base class? If so, what’s the best way to share?

• Does the new base class need unit tests, or is it ok for base classes to be tested through the tests of their children? Does it matter if the base class is abstract?

• Is it enough to ensure that classes A & B derive from the base class and ‘trust’ the unit tests for the base class to test the common behaviour (so the tests don’t need to be replicated in the child classes)? The tests for A & B only need to test they’re new/changed behaviour?

• Am I following totally the wrong approach having approximately one unit test class per real class?

I’ve taken different views at different times and the different approaches can have quite an impact on the ability to refactor the code, time taken to write tests etc. What approaches have people found works best?

Community
  • 1
  • 1
forsvarir
  • 10,749
  • 6
  • 46
  • 77

3 Answers3

4

Personally, given time, I tend to test all three (base and two derived). It shows that you're not inadvertently overriding the base methods and changing their behavior, and your inherited class still provides the expected semantics. If the behavior really doesn't change, then it could be as simple as a copy-paste job, but it provides more complete coverage.

Note the "given time" part, though. If time is an issue (and it always is), testing the base class or the inherited functionality would probably be lower priority. But testing is great inoculation against yourself, and makes you much more confident when refactoring later, so you're only shortchanging yourself, your customers, and/or your maintainers by not doing as complete coverage as you have time for.

However, pawning repetitive things like this off on dedicated testers or a QA team, if you have one, is perfectly acceptable. But buy them a beer sometimes :-) They make you look better!

Sdaz MacSkibbons
  • 27,668
  • 7
  • 32
  • 34
  • -1, no matter your time constraints, not doing proper unit testing will be more expensive than doing it. – CarlosZ Apr 10 '11 at 23:50
3

You might look at code coverage tools; they can show you if you're actually testing all of the code. Personally if I have a test covering the base class behavior and I"m not overriding that, I won't test it again. The goal is to have a code change (potentially) break only one test.

Don't feel the need to stick to one unit test class per real class. One unit test class per unique setup fixture is one way, and then there's the whole BDD school...

TrueWill
  • 25,132
  • 10
  • 101
  • 150
  • 2
    I've never really found code coverage tools to be particularly useful, without expending a lot of effort on them. Knowing that the tests run some code isn't the same as as knowing that the code is tested. – forsvarir Apr 11 '11 at 08:17
  • If I only have tests for common behaviour in the base class, how do I know 'class A/B' behave like 'base'? Are you saying that I don't really need to know? Do you test that A is derived from base? – forsvarir Apr 11 '11 at 08:24
  • @forsvarir: Agreed, coverage alone does not guarantee good tests. But if class Base has method X that is covered by tests and class Descendant does not override X, there's no need to re-test Descendant.X. Add tests for the behavior that varies. – TrueWill Apr 11 '11 at 15:23
  • @TrueWill: I think I largly agree, but the niggle is, what if a requirement comes along that could be made by extending class B, but instead a developer 'fixes' the base class. The behaviour of class A is also changed by the 'fix', which may or may not be right and there's no tests to catch it. – forsvarir Apr 12 '11 at 08:01
  • @forsvarir: Unit tests do not protect your code from fools. They guide design, check logic, provide a partial safety net for refactoring, verify some class-level behavior, catch some bugs, and provide limited regression tests. This is why you need layers of tests; in particular, end-to-end acceptance tests (which are not unit tests). – TrueWill Apr 12 '11 at 12:56
  • I guess it's a case of trying to find the right balance between protection and flexibility... I'm still not sure I'm there... – forsvarir Apr 14 '11 at 15:00
0

Refactoring test code is just as important as refactoring production code. Both should be treated as first class citizens. So if you are extracting public methods to the base class then that should have its own set of tests. If your test cases are designed properly where each tests tests one thing each then the test refactoring should be easy.

If you are extracting protected functionality then probably its it a slightly grey area. If test methods are new to the class then I would expect them to be tested in the derived class simply because they are probably there for the derived class to function properly. Ideally this should be kept to a minimum as the base class functionality becomes less obvious.

The derived classes then will have the remaining public methods tested in their own set of tests.

Again if you change the production code and not the tests then you should incorporate a coverage tool so you feel confident enough your tests are covered enough.

aqwert
  • 10,559
  • 2
  • 41
  • 61