What is unit test?
Let's start by defining "unit test." Unit testing is the testing of a small "unit" of code, typically performed by the developer of the code that's being tested. Ideally, the unit tests themselves are coded so that they can be run over and over again in an automated way.
For example, let's say you have a piece of code that is supposed to accept a date representing a birthday. Your application wants to find out the age (in days) of the person who has this birthday. The pseudo-code for this routine might look something like this:
If birthday NOT valid format
Return error message: Invalid date format
today = system date
Convert today and birthday to integers that represent days
If birthday > today
Return error message: Birthday cannot be in future
If birthday == today
Return message: Today is your Birthday! Happy Birthday!
today – birthday = age_in_days
Return message: You are age_in_days days old
Now, in order to unit test this code thoroughly, the developer would want to make sure each line of code is executed and validate that it performs as expected. In order to do that, he'd need tests in which the birthday was entered with an invalid format, tests where the birthday was in the future, a test where the birthday was the current date, and tests where the birthday was in the past. If the program accepted the birthday as input from the terminal, he might manually test by calling GetAge with various parameters such as GetAge(2/29/60), GetAge(10204020), GetAge(20/52/2011) and checking that the outputs are correct.
However, rather than manually entering tests and checking for validity, it would be better to write a program (Maybe GetAgeTest) that would execute with different values for birthday. Part of the test would include validation that the proper message (or output) was returned and then a pass or fail for each test. Failures might even automatically send out notifications to a designated contact.
Note that sometimes, as in this case, the results are going to vary because the system date will change. So for tests with valid dates, the age in number of days will increase over time. If the programmer hasn't taken this into account, the test case might report back a failure because the result is different from the day before. Thus, the programmer must make sure that their automated test cases accurately test for static as well as dynamic conditions.
Once these tests are written, they can easily be executed as regression tests. Later, if the GetAge function changes internally, GetAgeTest will ensure that all the test conditions are still executing correctly.
So what is test-driven development?
Test-driven development (TDD) is a type of unit test which originated with the agile methodology called Extreme Programming (XP). Similar to the above example, TDD uses automated tests that can then be used as regression tests whenever a new build is done. One difference, however, between TDD and traditional unit testing is that the test programs are written before the application code is written. In other words, it is the test that is driving the development or application code, rather than the other way around. The test, of course, will fail, until the application code is written which will fulfill the conditions of the test.
The traditional approach to software development is to do the design, code, and then test. TDD uses much more of an iterative approach. Rather than wait until the entire design is completed to start coding, and wait until the code is complete to start testing, test, design, and development are all done in very short iterations.TDD helps designs evolve.
- Write a test
- Run tests; watch the new test fail
- Make the test pass
- Refactor to remove duplication
The first step, writing the test, requires the developer to determine the outcome of a small segment of code. The test, in a sense, becomes the documentation of the requirement. Rather than investing in what Johansen terms the "Big Design Up Front" of traditional development, the design evolves in the act of writing the code that will satisfy the conditions of the test. Refactoring is the term used for removing duplication or improving design. As long as the test still passes, the internal code can be optimized by refactoring.
For more information see the SSQ Learning Guide, which has a large compilation of articles on unit testing and TDD.