TDD, BDD, Unit Testing, Top-Down, Bottom-Up, Wus Up?

Tuesday, February 17, 2009

As developers, we all have pretty much the same goals in mind. In general, these are to write good software quickly. There are many definitions of what is good software, but for our purposes let's define it as software that (1) has a low number of defects, (2) software that can be delivered in time, (3) software that can adapt quickly to changing requirements, and (4) software that can be easily understood by peers (clean and well-designed). To that effect, however you get there is OK. Whether you use some form of automated testing, manual testing, or Kung Fu. If the end result is "good software", why should it matter?

Lately, there's been a lot of negative attention drawn to unit testing. The negativity seems to be indicator of a lack of understanding of where unit testing fits in, how it can be applied, and some weird assumptions that unit testing is some golden beacon. Many anti unit test folks talk about a "false sense of confidence" that comes along with unit testing. For us, the more tests we write, the more we realize what is missing. Frequently, confidence goes down as the number of tests goes up. We attribute this to the cognitive process of reasoning about an application under test. For us, this is a discreet value emerging from unit testing. We've found that tests help us to understand what needs to be written and frequently points out gaps, errors, and omissions. Others don't need or don't use unit testing the same way - they seem to feel it's a waste of time. Why should we judge your process if what you produce is "good"?

We think there's some definition problems, too. People seem to get confused about unit testing and TDD. Likewise, there's confusion about BDD and unit testing. To be clear, both TDD and BDD are processes. Unit testing is a testing target or technique, not a process. MXUnit and cfSpec are tools that support processes and techniques. MXUnit is a test harness that can be used to run many different kinds of tests. For example, if you have a BDD package you could use MXUnit to support both your TDD and BDD efforts:

function dogShouldSitWhenTold(){
var myRadBdd = createObject('component','com.foo.RadBDD');
var dog = myRadBdd.create('com.bar.Dog')
.should()
.sit()
.when()
.iSaySo();
}

function myComplexAlgorithmNeedsToWorkCorrectly(){
var algo = createObject('component','com.foo.Algorithm');
var a = [1,2,3,4,5,6];
var actual = algo.performSomethingComplex(a);
assertTrue( actual.isRad() );
}
The above shows two different approaches to testing. In addition, they test two different layers. The first one is a BDD-like approach to verify the dog's behavior by exercising various methods on the Dog object together. It tests how these methods interact. This is loosely referred to as interaction testing, behavior testing, or integration testing. This may support a use case requirement and be something that a customer may also understand. Whereas the second one (a clear unit test) targets a piece of code, a single method, responsible for some imaginary computation. This computational unit test is not intended to test how the object interacts with other objects; it's focus is on what the method is doing and how it affects the component's state. The method under test may not deliver any direct value to an end-customer, but it's role within the software may be critically supportive. To assume that this method will be thoroughly tested by some other object higher up in the stack may lead to defects. The best way to test something like this is in isolation and pass it a variety of test data that will exercise the method in both expected and unexpected ways.

The point is, that unit testing and interaction testing, in clear cases like this, show how two different approaches to testing can be used to address different aspects of software. Also, it shows how these two types of tests can be run using one tool. MXUnit provides you a platform to build the kinds of tests that best suit your development needs. MXUnit does not dictate how you should build tests, in what order they should be tested, what assertions to use, or if you should test units or object interactions. Whether you write your tests first or last, whether you express your tests as focused unit tests or interactions or both, or whether you prefer BDD to TDD or Role-Your-Own-DD, different tests and testing approaches address different aspects of software systems. Further, some testing approaches fit better into differing development cultures. Some projects may require more unit testing; others more functional tests, and yet others only manual testing may be required. It's up to you to decide what works best for your projects and how best to achieve the goal of "good" software.

MXUnit is committed to providing the best testing platform possible for ColdFusion developers.

Test and be Happy

the guys at mxunit dot org.

1 comment:

Jim Priest said...

Excellent explanation of how all this fits together!