Page 1 of 1

Orthogonal Test Organization

Posted: Sat Dec 05, 2009 8:35 am
by josh
Lets say you had a SUT (system under test)

For fun lets make it a Animal class.

So the Animal class has this interface:

Code: Select all

walkForwards()
walkBackwards()
makeNoise()
Let's say this Animal gets injected (for testability) a configuration object in it's constructor, the config option provides paramaters

Code: Select all

walkSpeed
canWalkBackwards
noiseStyle
 
Let's say you will need to cover these code paths

Code: Select all

A) walkForwards WILL walk the appropriate speed
B) walkBackwards WILL walk the appropriate speed
C) walkBackwards WILL throw an exception if this animal can not Walk Backwards
D) makeNoise should use the right noise style
I might create these test classes:

Code: Select all

 
AnimalWalkTest test class will test paths A & B & C
AnimcalNoiseTest will test path D
Now what if I need another configuration option, canWalk (controls forwards & backwards with 1 setting)

So there is a new path

Code: Select all

E) walkForwards WILL throw an exception if animal is NOT a Walker
The new test classes would be

Code: Select all

AnimalWalkTest test class will test paths A & B
AnimcalCanWalkTest will test paths C & D & E
AnimcalNoiseTest will test path D
Now what if I need to make yet another setting that was orthogonal with the existing settings, how do you keep from a test class explosion, without violating single responsibility principle in the test class (each test class should only declare the test utility methods it uses).

I guess this would be more of a functional test suite, so I want to be able to test each possible path, lets say I needed to test all these paths. How do you organize the tests?

Code: Select all

 
walkSpeed = happy path
canWalkBackwards = True
noiseStyle = happy path
canWalk = True
 
walkSpeed = boundary path
canWalkBackwards = True
noiseStyle = happy path
canWalk = True
 
walkSpeed = happy path
canWalkBackwards = False
noiseStyle = happy path
canWalk = True
 
walkSpeed = happy path
canWalkBackwards = True
noiseStyle = boundary path
canWalk = True
 
walkSpeed = boundary path
canWalkBackwards = True
noiseStyle = boundary path
canWalk = True
 
walkSpeed = boundary path
canWalkBackwards = False
noiseStyle = boundary path
canWalk = True
 
 
walkSpeed = happy path
canWalkBackwards = True
noiseStyle = happy path
canWalk = False
 
walkSpeed = boundary path
canWalkBackwards = True
noiseStyle = happy path
canWalk = False
 
walkSpeed = happy path
canWalkBackwards = False
noiseStyle = happy path
canWalk = False
 
walkSpeed = happy path
canWalkBackwards = True
noiseStyle = boundary path
canWalk = False
 
walkSpeed = boundary path
canWalkBackwards = True
noiseStyle = boundary path
canWalk = False
 
walkSpeed = boundary path
canWalkBackwards = False
noiseStyle = boundary path
canWalk = False
In a real example you could say get rid of the Noise path, its decoupled from walking, but lets make it a real example and say we also need to test a ton of behavior like the animal making noises every N steps it takes, etc... ( so suddenly you are multiplying the # of paths again and again)

Would you have

Code: Select all

AnimalWalkForwardsNoiseTestClass
AnimalWalkForwardsSpeedTestClass
AnimalWalkBackwardsNoiseTestClass
AnimalWalkSpeedNoiseTestClass
etc....

How do you keep from littering your namespace?