TestFirstDeliverable
Test-First Programming
Test-First is a method of programming where the test for the program is actually created before the code itself. “Extreme Programming champions the use of tests as a development tool. Given an object, devise tests for all interesting methods even before you program them. This means that:
you are forced to define precisely what a method does
you know where to begin writing a method
you know when you are done writing a method
you know the minimal scaffolding needed to run a method
like scientists, you target reproducible results
This means that you will recognize dependencies among the objects early, and work to minimize them. You will have broken up your work, so you know at the beginning of the day what you have to do, and you know when you are done.
This style of programming gives you a continuity of development that is inaccessible otherwise. You find mistakes early enough that the dreaded day of debugging becomes a distant memory. The effects of bugs in unrelated modules get detected when designing test setup.
You get to use debuggers as sharp, pointed, precise tools rather than as shovels for digging for mistakes among mounds of code. When a test fails, you set a breakpoint and trace. Don't try to second guess the computer. If the tests are fine enough, and the scaffolding small enough, debug runs end very quickly. You can think of this as Computer Aided Code Inspection.
It also means that you can refactor confidently, knowing that your tests will protect you from mistakes. In turn, refactoring constantly keeps design warts from becoming cancers.
When adding new functions, devise a new test. If the new feature suggests a change in the design, do it. Your earlier tests let you do it without an Oh God feeling lurking in the background. Then get the new test going.” (Cunningham, 2005)
Test-first is important because it takes the guess work and assumptions out of writing code. “Creating a unit test helps a developer to really consider what needs to be done. Requirements are nailed down firmly by tests. There can be no misunderstanding a specification written in the form of executable code.You also have immediate feedback while you work. It is often not clear when a developer has finished all the necessary functionality. Scope creep can occur as extensions and error conditions are considered. If we create our unit tests first then we know when we are done; the unit tests all run.There is also a benefit to system design. It is often very difficult to unit test some software systems. These systems are typically built code first and testing second, often by a different team entirely. By creating tests first your design will be influenced by a desire to test everything of value to your customer. Your design will reflect this by being easier to test. There is a rhythm to developing software unit test first. You create one test to define some small aspect of the problem at hand. Then you create the simplest code that will make that test pass. Then you create a second test. Now you add to the code you just created to make this new test pass, but no more! Not until you have yet a third test. You continue until there is nothing left to test. The coffee maker problem shows an example written in Java.The code you will create is simple and concise, implementing only the features you wanted. Other developers can see how to use this new code by browsing the tests. Input whose results are undefined will be conspicuously absent from the test suite.” (Wells, 2000)
Sean Shubin offers a word of caution while using code first. “As project complexity grows, you may notice that writing automated tests gets harder to do. This is your early warning system of overcomplicated design. Simplify the design until tests become easy to write again, and maintain this simplicity over the course of the project.” (Shubin, 2004)
Here is a benchmark for the steps to complete proper test-first code.
It's important to be methodical and consistent when developing unit tests for a piece of software. The development of a unit test should be treated with the same care that's taken when developing other code. A number of best practices should be employed to keep the unit tests manageable and useful.
1. All unit test classes should have a main(). Although the unit tests will normally be run as a group, there are times when it's necessary to run just one of the unit test classes to isolate a bug. Having a main method that just calls the junit.textui.TestRunner.run method makes that easy.
2. Make sure your test cases are independent. As the test cases in a unit test are developed, it's important to make sure that each test case can be run independently. This is especially true when testing database applications where the state of the database at the start of a test case must be consistent in order for the test to be effective. It's possible that a test case will incorrectly handle an exception that occurs during a test run and corrupt the database. This can cause all subsequent test cases to fail. Nothing is more annoying than spending hours debugging an application that appeared to be failing because the test cases were not set up properly.
Don't count on the test cases being run in a certain sequence because the JUnit framework doesn't guarantee the order of test method invocations. The setup and teardown methods are called before and after each test case is run. They can be used to set up a database to a known state before a test is run and restore it to a known state after the test is complete. It's also very important to make sure that test methods make every effort to clean up after themselves in every conceivable exit condition.
3. Minimize the amount of code in setup/teardown methods. Since the setup and teardown methods are called before and after each test case, it's important to make sure they're efficient. Some test suites can contain 50, maybe even 100, test cases. This includes making sure that these methods don't output unnecessary log messages and make it hard to follow the execution path. Making these methods efficient will also speed up the time it takes to run the test, which allows the tests to be run more often.
4. Use fail() instead of assert() to catch test case error. If the test case fails because of an error or exception as opposed to an assert that fails, it's better to use the Assert.fail(String msg) than assert(true,false). This will make it easier to wade through the debug statements and can decrease the amount of time required to understand and fix a problem. Using Assert.fail(String msg) with a description of the failure instead of calling assert(boolean boolValue) can more directly describe the type of failure occurring. The message passed to fail(String msg) will be printed out by the TestRunner in the stack track of failures. This allows the developer to quickly see what's really failing in a test case.
5. Properly document test code. As with any code being developed, it's important that the test code be properly documented with Javadocs. Just as it's easier to maintain well-documented code, well-documented test cases are easier to update.
(Hammell, 2005)
Works Cited
Wells, Don, Code the Unit Test First, online at
http://www.extremeprogramming.org/rules/testfirst.html , 2000
Shubin, Sean, Test First Guidelines, online at
http://www.xprogramming.com/xpmag/testFirstGuidelines.htm , 2004
Cunningham, Ward, Test Driven Programming, online at
http://c2.com/cgi/wiki?TestDrivenProgramming 2005
Hammell, Thomas, Test First, Code Later, online at
http://www.sys-con.com/story/?storyid=36842&DE=1,2005
Test-First is a method of programming where the test for the program is actually created before the code itself. “Extreme Programming champions the use of tests as a development tool. Given an object, devise tests for all interesting methods even before you program them. This means that:
you are forced to define precisely what a method does
you know where to begin writing a method
you know when you are done writing a method
you know the minimal scaffolding needed to run a method
like scientists, you target reproducible results
This means that you will recognize dependencies among the objects early, and work to minimize them. You will have broken up your work, so you know at the beginning of the day what you have to do, and you know when you are done.
This style of programming gives you a continuity of development that is inaccessible otherwise. You find mistakes early enough that the dreaded day of debugging becomes a distant memory. The effects of bugs in unrelated modules get detected when designing test setup.
You get to use debuggers as sharp, pointed, precise tools rather than as shovels for digging for mistakes among mounds of code. When a test fails, you set a breakpoint and trace. Don't try to second guess the computer. If the tests are fine enough, and the scaffolding small enough, debug runs end very quickly. You can think of this as Computer Aided Code Inspection.
It also means that you can refactor confidently, knowing that your tests will protect you from mistakes. In turn, refactoring constantly keeps design warts from becoming cancers.
When adding new functions, devise a new test. If the new feature suggests a change in the design, do it. Your earlier tests let you do it without an Oh God feeling lurking in the background. Then get the new test going.” (Cunningham, 2005)
Test-first is important because it takes the guess work and assumptions out of writing code. “Creating a unit test helps a developer to really consider what needs to be done. Requirements are nailed down firmly by tests. There can be no misunderstanding a specification written in the form of executable code.You also have immediate feedback while you work. It is often not clear when a developer has finished all the necessary functionality. Scope creep can occur as extensions and error conditions are considered. If we create our unit tests first then we know when we are done; the unit tests all run.There is also a benefit to system design. It is often very difficult to unit test some software systems. These systems are typically built code first and testing second, often by a different team entirely. By creating tests first your design will be influenced by a desire to test everything of value to your customer. Your design will reflect this by being easier to test. There is a rhythm to developing software unit test first. You create one test to define some small aspect of the problem at hand. Then you create the simplest code that will make that test pass. Then you create a second test. Now you add to the code you just created to make this new test pass, but no more! Not until you have yet a third test. You continue until there is nothing left to test. The coffee maker problem shows an example written in Java.The code you will create is simple and concise, implementing only the features you wanted. Other developers can see how to use this new code by browsing the tests. Input whose results are undefined will be conspicuously absent from the test suite.” (Wells, 2000)
Sean Shubin offers a word of caution while using code first. “As project complexity grows, you may notice that writing automated tests gets harder to do. This is your early warning system of overcomplicated design. Simplify the design until tests become easy to write again, and maintain this simplicity over the course of the project.” (Shubin, 2004)
Here is a benchmark for the steps to complete proper test-first code.
It's important to be methodical and consistent when developing unit tests for a piece of software. The development of a unit test should be treated with the same care that's taken when developing other code. A number of best practices should be employed to keep the unit tests manageable and useful.
1. All unit test classes should have a main(). Although the unit tests will normally be run as a group, there are times when it's necessary to run just one of the unit test classes to isolate a bug. Having a main method that just calls the junit.textui.TestRunner.run method makes that easy.
2. Make sure your test cases are independent. As the test cases in a unit test are developed, it's important to make sure that each test case can be run independently. This is especially true when testing database applications where the state of the database at the start of a test case must be consistent in order for the test to be effective. It's possible that a test case will incorrectly handle an exception that occurs during a test run and corrupt the database. This can cause all subsequent test cases to fail. Nothing is more annoying than spending hours debugging an application that appeared to be failing because the test cases were not set up properly.
Don't count on the test cases being run in a certain sequence because the JUnit framework doesn't guarantee the order of test method invocations. The setup and teardown methods are called before and after each test case is run. They can be used to set up a database to a known state before a test is run and restore it to a known state after the test is complete. It's also very important to make sure that test methods make every effort to clean up after themselves in every conceivable exit condition.
3. Minimize the amount of code in setup/teardown methods. Since the setup and teardown methods are called before and after each test case, it's important to make sure they're efficient. Some test suites can contain 50, maybe even 100, test cases. This includes making sure that these methods don't output unnecessary log messages and make it hard to follow the execution path. Making these methods efficient will also speed up the time it takes to run the test, which allows the tests to be run more often.
4. Use fail() instead of assert() to catch test case error. If the test case fails because of an error or exception as opposed to an assert that fails, it's better to use the Assert.fail(String msg) than assert(true,false). This will make it easier to wade through the debug statements and can decrease the amount of time required to understand and fix a problem. Using Assert.fail(String msg) with a description of the failure instead of calling assert(boolean boolValue) can more directly describe the type of failure occurring. The message passed to fail(String msg) will be printed out by the TestRunner in the stack track of failures. This allows the developer to quickly see what's really failing in a test case.
5. Properly document test code. As with any code being developed, it's important that the test code be properly documented with Javadocs. Just as it's easier to maintain well-documented code, well-documented test cases are easier to update.
(Hammell, 2005)
Works Cited
Wells, Don, Code the Unit Test First, online at
http://www.extremeprogramming.org/rules/testfirst.html , 2000
Shubin, Sean, Test First Guidelines, online at
http://www.xprogramming.com/xpmag/testFirstGuidelines.htm , 2004
Cunningham, Ward, Test Driven Programming, online at
http://c2.com/cgi/wiki?TestDrivenProgramming 2005
Hammell, Thomas, Test First, Code Later, online at
http://www.sys-con.com/story/?storyid=36842&DE=1,2005
