The right tools will get you on the right track with TDD I once worked on a project that placed a low priority on process improvement. Everyone complained that building various components of the system took too long, and running regression tests took too long, but no one was willing to change the process. The most common excuse was, “Yes, we know that is a problem, but we are too busy to fix it right now.” What amazed those of us working on this project most was the complete lack of value placed on the amount of time it took a developer to complete everyday development tasks.Compiling and testing are tasks developers perform almost every single day—these tasks should be quick and painless. On this project, however, building just one jar file consisting of a few thousand source files took more than 15 minutes. Running tests involved pulling specific versions of libraries from the CM system, reading directions on multiple Websites, and running five to ten different shell and Perl commands. Once the tests were finished, the developer had to manually decipher test failures hidden in text files with cryptic names. I don’t even like to recall how long it took for developers on this project to run the tests.After a little investigation, the team discovered the build mechanism for that “slow building” library was a poorly written collection of make files. The make files were written such that a separate javac process was forked to compile each source file in the entire library. A simple conversion from the make files to an Ant build file dropped the build time to less than a minute. When we looked into the acceptance tests, we discovered the entire regression test suite consisted of functional tests. By converting the functional tests to unit tests, we were able to cut regression tests that ran for hours down to less than a half hour. As software engineers, we often deal with performance requirements and end-user acceptance requirements. Why should the development process be any different? Quite frankly, it shouldn’t. Development projects should have realistic requirements for how long a build and test cycle should take. Metrics should be collected on a regular basis, so corrective action can be taken as soon as the build and test times exceed requirements.Selecting the right toolsHow does one go about implementing a test-driven development (TDD) process? What do you need to practice TDD? The key to TDD is automating the code-compile-test process with the right set of tools! We’ve all heard the expression, “Use the right tool for the job.” This expression definitely holds true for TDD. Without the right tools, implementing and maintaining a TDD process can be time consuming and painful. It’s a standard practice for development teams to use common build scripts or make files for compiling source code. A good TDD process takes this practice a step further by implementing common scripts that combine the compile and test steps into a standard development process. So, what are the right tools?First and foremost is the test framework itself. A test framework supplies a class hierarchy, convenience APIs, and a common language for writing and running repeatable tests. Development teams would probably end up implementing such a framework if they do not use one that is publicly available. Without such a framework, developers would have to re-create common test harnesses and utility classes just to run their own tests. Depending on the requirements of the application you are building, your test and development process may also require additional framework extensions to cover protocols such as HTTP. The sample application built throughout the remainder of this book is a J2EE-based Football Pool application for automating your favorite office pool. It must run on multiple operating systems and multiple application servers. For now, you only need to be concerned with how the type of application drives requirements for the TDD process. Since the application is a J2EE application, you know your development and test environment must include a J2SE SDK and a J2EE application server. Adding the application-specific elements to your process requirements of an automated code-compile-test process results in the following TDD requirements:A Java test framework supporting common assertionsA cross-platform scripting tool to start/stop tests and dependent processesA cross-platform mechanism for building and packaging Java source codeAn IDE that supports refactoringA J2EE application serverA J2SE SDKAlmost every development project goes through a tool analysis process to determine what tools are needed to develop the software. Unfortunately, spending a few chapters comparing tool choices for each of the preceding requirements would severely limit the amount of TDD material I could cover in this book. The following section lists some recommended tools for implementing the preceding TDD requirements. Each tool is briefly described along with a short explanation of why the tool was chosen for the given requirement. I will mention several alternative approaches related to some of these tools, with a sentence or two on why those approaches weren’t used here.Recommended toolsFollowing the Keep It Simple, Stupid (KISS) and You Aren’t Gonna Need It (YAGNI) rules of extreme programming (XP), each tool listed in the following sections fits into the TDD process by meeting one or more of the requirements in its simplest form. The following sections contain a brief description of each tool. JUnit JUnit is the primary test tool for satisfying the first requirement for a Java test framework. JUnit is the Java version of the xUnit architecture for unit- and regression-testing frameworks. Written by Erich Gamma and Kent Beck, it’s distributed as an open source project, includes the core test framework class hierarchy, and defines a common language for writing and running repeatable tests. JUnit uses reflection to examine the tests and code under tests. This allows JUnit to execute any method of any class and examine the results. With this framework, all developers on the project know how to write and execute tests, and interpret test results using the following nomenclature:TestCase: Abstract class for implementing a basic unit testTestSuite: Composite class for organizing and running groups of testsAssertions: For testing expected results (assertNotNull(..), assertEquals(..), assertSame(..), etc.)TestRunner: Graphical and text-based test runnersFailure: Indicates a checked test assertion failed (i.e., assertNotNull(..) returned false)Error: Indicates an unexpected exception or setup failure that stopped the testJUnit is widely accepted as a standard for unit testing in Java. Many of the available testing products on the market are either based on or extend JUnit. Also, many IDEs currently have built-in support for JUnit. The majority of unit tests written in this book are JUnit tests. HttpUnit HttpUnit, written by Russell Gold, is an open source Java library for black-box testing HTTP-based Web applications and servlets. The library supports testing applications containing the following:Basic and form-based authenticationState management with cookiesJavaScriptAutomatic page redirectionHttpUnit easily integrates with JUnit for fast development of HTTP-based functional tests. In fact, using HttpUnit with JUnit allows you to write tests for Web applications and entire Websites. HttpUnit provides methods that allow JUnit TestCases to examine returned HTML content as text, an XML DOM (Document Object Model), or containers of forms, tables, and links, as well as provide a way to submit forms, press buttons, and follow links. It also contains a servlet test framework, ServletUnit, which can be used to help develop servlets. Ant You can meet requirements 2 and 3 (a cross-platform scripting tool to start/stop tests and dependent processes, and a cross-platform mechanism for building and packaging Java source code) by using Ant. Ant is a cross-platform build and scripting tool. It was developed as a Java and XML-based open source project by the Apache Software Foundation. Similar to make, gnumake, and nmake files, Ant build files define project dependencies, classpaths, and build targets. Unlike make, however, Ant is designed for Java with build scripts written as XML files, which can be validated with an XML editor. Also unlike make, Ant dependencies are simple dependencies and are not used to bring targets up to date like make does. Ant is written in Java and can easily be extended to create custom tasks whenever necessary. A default Ant install supports a number of Java-specific tasks (a task being like a command) plus many platform-independent file management tasks for automating the entire build process. Following is a brief list of some of the useful tasks available:Compile and run JavaCreate, copy, delete, and move files and directories (with regular expression pattern-matching)Create archive files (.jar, .war, .ear, .zip, etc.)Connect to configuration management systems (CVS, StarTeam, Visual SourceSafe, etc.)Run JUnit testsA key feature of Ant is its integration with the JUnit test framework listed previously. With Ant, you can run multiple sets of JUnit and HttpUnit tests, aggregate the test results, and publish HTML test reports for the entire process to a Web server for immediate viewing by clients or management. Ant scripts control the build and test process for the majority of examples throughout this book. You could make an argument for using a combination of Perl and make files to control the build process, but I see no reason to introduce two different tools to accomplish what Ant can do by itself. This is a Java project, staffed by Java developers. While Perl is a powerful programming language, and make has been used in software for decades, they are two additional specialized skill sets that are not required for this project. Remember that the development and test environment must be maintained over time just as the product source and test code.Refactoring IDE All of the following IDEs meet requirement 4—an IDE that supports refactoring. This is not an exhaustive list, just a list of popular IDEs that you may be familiar with and meet our needs. JDeveloperIDEAEclipseWhile Java development can certainly be accomplished with any text or Java source code editor, maintaining a large base of source code and the accompanying tests is easier to accomplish with an IDE that supports automated refactoring (see Refactoring: Improving the Design of Existing Code, by Martin Fowler et al. [Addison-Wesley, 1999]). One of the common arguments for writing tests is the amount of work required to create and maintain such tests. An IDE that supports refactoring gives developers the necessary tool to accomplish tasks that would otherwise be tedious and time consuming.Take changing a method signature, for example. Assume that a change in requirements forces a change to the method signature in a common interface. If this method is used in more than 100 lines in various source files, this change could take hours. With a refactoring IDE, this change is completed and tested in just minutes. All three IDEs listed previously have automated features for accomplishing common refactoring techniques such as the following:Extract methodIntroduce variableRename/move packageExtract superclassChange method signatureThe list of IDEs shown earlier is not intended to be all encompassing. There may be other IDEs on the market that support refactoring. The three listed are either open source or are available as a free trial download. J2EE application serverRequirement 5 (the application server) is a development, testing, and production requirement. Since the sample application is a J2EE application, it must ultimately run on a J2EE application server. From a unit-testing purist standpoint, the goal for developers should be to write all tests as unit tests, and limit tests that run on the application server to functional tests and end-user acceptance tests.The sample application should be capable of running on other J2EE application servers with few or no changes. J2SE Java Development Kit Requirement 6 is met by any 1.4.x version of the J2SE JDK. It goes without saying that Java development requires a JDK. It is listed here for completeness, and to avoid emails from angry first-time developers complaining that their build script fails to execute.TDD tool summary These tools are not absolutely required to practice TDD. The selection of tools does, however, prove that a development team can implement a TDD process in a cost-effective and efficient manner. I would further argue that a typical development team would unnecessarily waste a huge chunk of development time building a fraction of the functionality provided by these tools in any attempt to implement a homegrown TDD solution. Why reinvent the wheel, when there is a perfectly good set of supported products to kick around for FREE?Setting up your TDD environmentFor your convenience and to help speed up the initial setup process, a prepackaged archive of the TDD environment for the sample application is available for download from Apress. For complete instructions on how to download and run the code, see Appendix A. Ant will be the main tool used to build and run the examples, so the next section provides a brief introduction to Ant in case you’re not familiar with it.A quick introduction to AntWriting and running an Ant build file is similar to writing and running a Perl, batch, or shell script. The major difference with Ant is that build files (scripts) are written in XML. The following build file is the ubiquitous “Hello World” example written as an Ant script: <project name="HelloWorld" default="say-hello" basedir="."> <property name="hello.message" value="Hello World"/> <target name="say-hello" depends="set-time"> <echo message="${hello.message} today is a beautiful ${time.of.day}"/> </target> <target name="set-time"> <tstamp> <format property="time.of.day" pattern="hh:mm aa " locale="en"/> </tstamp> </target> </project> Each build file contains one project element and at least one (default) target element. The preceding example build file defines the project "HelloWorld" with a default target named "say-hello" and a second target named "set-time". The project also defines a property (Ant’s version of a variable) named "hello.message". The "say-hello" target contains one task element named echo. The echo task, believe it or not, echoes (prints out) the value in its message attribute. The "set-time" target contains the tstamp task, which is Ant’s mechanism for creating date and time stamps. The result of the tstamp task is set in the property "time.of.day". The echo message attribute in the example contains the string ${hello.message} today is a beautiful ${time.of.day}.Ant uses a Unix syntax style to represent the “value of” a variable. Referencing ${hello.message} results in the value "Hello World". The message attribute also contains a reference to the property ${time.of.day}, but "time.of.day" is not set in the target "say-hello".If the property "time.of.day" is set in the target "set-time", will the "say-hello" target have access to this property? What does the echo task print at runtime? The answer lies in how the depends attribute of the target element works within Ant. The "say-hello" target is declared as follows: <target name="say-hello" depends="set-time"> The depends attribute in Ant means “run the target(s) listed here first.” Since depends="set-time", the "set-time" target gets run before the "say-hello" target, resulting in the following message being printed:Hello World the time is: 11:01 PMIf the depends attribute was not set to "set-time", the output would have been:Hello World the time is: ${time.of.day} The complete output from executing ant with no arguments or executing ant say-hello on this example build file results in the same message being printed out at runtime: // No arguments - runs the default target in the build.xml file <install-dir>/TDD/examples/ch2>ant Buildfile: build.xml set-time: say-hello: [echo] Hello World the time is: 11:12 PM BUILD SUCCESSFUL Total time: 1 second // Passing "say-hello" as the target argument > ant say-hello // Runs the "say-hello" target in the build.xml file Buildfile: build.xml set-time: say-hello: [echo] Hello World the time is: 11:12 PM BUILD SUCCESSFUL Total time: 1 second Two final important facts concerning Ant properties:Properties are immutable: Once a property is set, its value cannot be changedProperties are global: Once a property is set in a project file, its value is accessible to all targets and tasks executing after the value is setAnt summaryComparing Ant to other programming or scripting languages, targets are similar to methods or functions. They get named by the author of the file, and contain one or more logic and/or command statements such as a condition or task that control the actual work executed within the target. Variables can be defined globally within a project, or within a target by property elements. A target is invoked directly or implicitly when running the ant shell from the command line. There are many features of Ant not covered in this basic introduction. Refer to the official Ant documentation on Ant’s Website or purchase a copy of Java Development with Ant by Erik Hatcher and Steve Loughran (Manning Publications, 2002) for a more thorough Ant education.The sample build.xml fileNow that you have a basic understanding of what an Ant script looks like and how it executes, it’s time to review the build.xml script for the sample application. A compressed view of the samples build file shows the following targets: <project name="tdd_build" default="test"> <target name="init"> <target name="clean"> <target name="compile-tests" depends="compile-src"> <target name="compile-src" depends="init"> <target name="test" depends="compile-tests" <target name="all" depends="clean,test"/> </project> The default target is "test", which depends on "compile-tests", which in turn depends on "compile-src", which itself depends on "init". Therefore the execution order isinitcompile-srccompile-teststestYou should see the same execution order of targets in the build output when the default Ant target is run: <install-dir>/TDD>ant Buildfile: build.xml init: compile-src: [javac] Compiling 6 source files to <install-dir>/TDDbuildclasses compile-tests: [javac] Compiling 1 source file to <install-dir>/TDDbuildtestclasses test: [junit] TEST tdd.TddTestSuite FAILED BUILD FAILED The compile steps should succeed, but the test and overall build should fail because you haven’t yet implemented the functionality needed to make the test pass. What is important at this point is the simplicity and power of this little Ant script. A few Ant targets and dependency settings are the building blocks for this automated compile-and-test development process. The Ant targets define what gets run, and the dependencies control the order of execution. Setting the default target to "test" ensures that all tests get run every time the build file is executed. You have to manually run another target in the build file to bypass the execution of the tests.Thomas Hammell is a senior developer with Hewlett-Packard and part of the Open Call Business Unit, which develops telecom networks infrastructure software. Hammell has more than 18 years of experience developing software. He has published numerous articles on Java topics, ranging from Swing development to unit testing. Hammell also lectures frequently on Java topics. He holds a bachelor’s degree in electrical engineering and a master’s degree in computer science from Stevens Institute of Technology. Russell Gold is a senior developer with Oracle, currently working on OC4J, Oracle’s J2EE-compliant application server. Gold has been developing software for more than 24 years. He authors and maintains the popular open source project, HttpUnit, a library for interacting with and testing Web applications from Java programs. Tom Snyder is a senior developer with Oracle and works on Oracle’s J2EE application server. Snyder has more than 10 years of experience developing software in the telecom, insurance, and application server areas. He holds a bachelor’s degree in computer and information sciences from Temple University. App TestingSecurityJava