1.3 Open Source Toolkit

Open source tools have been with us for a long time, but did not always enjoy widespread acceptance within the corporate environment. This has all changed in the past few years, as free tools became increasingly powerful and popular. In many cases, open source tools have no commercial equivalent. In others, commercial tools have embraced open source tools due to popular demandalthough you may have to purchase the most expensive enterprise edition to get these features. This is ironic because Ant and JUnit are free.

In this section, we introduce the tools used throughout this book. While we have no reason to suggest that you avoid commercial tools, we believe you can achieve the same end result without an expensive investment in tools.

1.3.1 Version Control

Version control tools are an essential building block of any software development project, so much so that we assume you are familiar with the basic concepts. We do not cover any specific tool in this book; however, we do want to spend a few moments pointing out how tools like CVS fit into an XP toolkit.

CVS keeps a master copy of each file on a shared directory or server, known as the repository. The repository keeps a history of all changes to each file, allowing you to view a history of changes, recover previous revisions of files, and mark particular revisions with tag names. In a nutshell, tools like CVS allow an entire team to update and maintain files in a predictable, repeatable way.

Each programmer has a copy of the entire code base on his computer, and makes changes locally without affecting other programmers. When the current task is complete, the programmer commits the modified files back to the CVS repository. The newly revised files are then visible to other programmers, who can choose to update to the new revisions whenever they are ready.

Regardless of whether you are using CVS or some other tool, two key principles always apply. These principles are:

  • Work in small steps.

  • Stay in sync with the shared repository.

Because of the pair programming required by XP, working in small steps is a necessity. You cannot switch programming partners several times per day if you work on tasks that take several days to complete. A key to XP success is your ability to break down a project into smaller tasks that can be completed within a few hours. Working in small steps also helps when using CVS (or any other version control tool) because your personal workspace does not get too far out of sync with the repository.

With CVS, multiple programmers on the team may work on the same files concurrently. When this happens, you must merge changes and resolve conflicts before committing your modified code to the repository. The best way to minimize potential for conflicts is to perform frequent updates. If a programmer does not get the latest code for days and weeks at a time, she increases the likelihood of conflict with work done by other team members.

While CVS allows concurrent edits to the same files, other version control tools force programmers to lock files before making changes. While exclusive locking seems safer than concurrent editing, it can impede development if other team members are unable to make changes. Again, working in small steps is the best way to avoid problems when working with locking version control tools. If each programmer only locks a few files at a time, the likelihood of lock contention is greatly reduced.

1.3.2 Ant

Ant, covered in Chapter 3, is a Java replacement for platform-specific make utilities. Instead of a Makefile, Ant uses an XML buildfile typically named build.xml. This buildfile defines how source code is compiled, JAR files are built, EAR files are deployed to servers, and unit tests are executed. Ant controls all aspects of the software build process and guarantees that all developers have a common baseline environment.

In the XP model, all programmers on the team share code. Programmers work in pairs that constantly shuffle. XP shuns the idea of certain individuals owning different frameworks within a system. Instead, any programmer is allowed to work on any piece of code in the application in order to finish tasks. Shared code spreads knowledge and makes it easier for people to swap programming partners. Sharing code also coerces people into writing tests, because those tests provide a safety net when working in unfamiliar territory.

Ant is important to XP because you cannot afford to have each developer compiling different parts of the system using different system configurations. Individual classpath settings might mean that code compiles for one team member, but fails to compile for everyone else. Ant eliminates this class of problem by defining a consistent build environment for all developers.

Ant buildfiles consist of targets and tasks. Targets define how developers use the buildfile, and tasks perform the actual work, such as compiling code or running tests. You generally begin by writing a basic Ant buildfile with the following targets:

prepare

Creates the output directories which will contain the generated .class files.

compile

Compiles all Java source code into the executable.

clean

Removes the build directory and all generated files, such as .class files.

junit

Searches for all unit tests in the directory structure and runs them. Tests are files following the Test*.java naming convention.[4]

[4] You can adopt whatever naming convention you wish; we chose Test*.java for this book.

This is a good start, and will certainly be expanded upon later. A critical feature of this Ant buildfile is the fact that it should define its own classpath internally. This way, individual developers' environment variable settings do not cause unpredictable builds for other developers. You should add the Ant buildfile to your version control tool and write a few simple classes to confirm that it runs successfully.

The other developers on the team then get the Ant buildfile from the version control repository and use it on their own computers. We also recommend that you place all required JAR files into version control,[5] thus allowing the Ant buildfile to reference those JAR files from a standard location.

[5] You normally don't put generated code into CVS, but third-party JAR files are not generated by you. Instead, they are resources required to build your software, just like source files.

1.3.3 JUnit

Automated unit testing is one of the most important facets of XP and a central theme throughout this book. JUnit tests are written by programmers and are designed to test individual modules. They must be designed to execute without human interaction. JUnit is not intended to be a complete framework for all types of testing. In particular, JUnit is not well-suited for high-level integration testing.

Instead, JUnit is a programmer-level framework for writing unit tests in Java. Programmers extend from the TestCase base class and write individual unit tests following a simple naming convention. These tests are then organized into test suites and executed using a text or graphical test runner.

JUnit is a simple framework for writing tests, and it is easily extended. In fact, JUnit is the starting point for several of the other testing tools covered in this book. From one perspective, the JUnit API is a framework for building other, more sophisticated testing frameworks and tools.

Tools like CVS, JUnit, and Ant become more powerful when everyone on the team uses them consistently. You might want to talk to the other programmers on your team and come up with a set of guidelines for adding new features to your application. The following list shows one such approach for adding a new feature to a system:

  1. Update your PC with the latest source files from the CVS repository. This minimizes the chance of conflicts once you are finished.

  2. Write a unit test using JUnit. Try to execute one facet of the new functionality that does not yet exist. Or, write a test to expose a bug that you are trying to fix.

  3. Run the test using JUnit and Ant by typing ant junit. The junit Ant target is defined with a dependency on the compile target, so all code is automatically compiled.

  4. After watching the test fail, write some code to make the test pass.

  5. Run the test again by typing ant junit. Repeat steps 2-5 until the task is complete and all tests pass. The task is complete when you have written tests for anything you think might break and all tests pass.

  6. Perform another CVS update to ensure you are up-to-date with all recent changes. This is a critical step because the CVS repository may have changed while you were working on your task.

  7. Run ant clean junit in order to perform a full build.

  8. If all code compiles and all tests pass, commit changes to CVS and move to the next task. Otherwise, go back and fix whatever broke before committing changes to CVS.

It is important for every developer to follow these steps, because you are using XP and practicing pair programming. Each of the team members takes turn pair programming with another person and each of you is allowed to make changes to any part of the code. Because you are all constantly updating a shared code base, you rely on the suite of automated unit tests along with a consistent build environment to immediately catch errors.

Provided everyone follows the process, it is generally easy to fix problems when a test starts failing. Because all tests passed before you made your changes, you can assume that the most recent change broke the test. If you work in small steps, the problem is usually (but not always!) easy to fix.

On the other hand, things get really ugly when a programmer commits changes to CVS without first running the tests. If that programmer's change broke a test, then all other programmers on the team begin to see test failures. They assume that they broke the test, and waste time debugging their own code. For a team of ten programmers, this may mean that ten programmers spend one hour each tracking down the problem, only to find that it wasn't their fault in the first place. Had that first programmer run the tests locally, he may have been able to fix the problem in a few minutes rather than wasting ten hours.[6]

[6] If the shared code base breaks frequently, programmers may begin to ignore the errors. This causes a snowball effect when they quit running tests and check in even more bad code. Pair programming helps avoid these breakdowns in diligence.

1.3.4 HttpUnit and Cactus

HttpUnit, covered in Chapter 5, is a framework for testing web applications. HttpUnit isn't built with JUnit; however, you do use JUnit when testing with HttpUnit. HttpUnit tests execute entirely on the client machine and access a web server by sending HTTP requests. In this fashion, HttpUnit simulates a web browser hitting a web site. Although you typically use JUnit when working with HttpUnit, the tests you write are more correctly considered "functional" tests rather than "unit" tests. This is because HttpUnit can only test a web application from the outside view, instead of unit-testing individual classes and methods.

A closely related tool is Cactus, covered in Chapter 7. Cactus is significantly more complicated than HttpUnit, and is built on top of JUnit. Cactus tests allow for true unit testing of web applications, with specific types of tests for servlets, JSPs, and servlet filters. Unlike HttpUnit, Cactus tests execute on both client and serversimultaneously. The client portion of the test acts something like a web browser, issuing requests to the server portion of the test that acts more like a unit test. The server then sends a response back to the client portion that then interprets the results.

Cactus can also make use of the HttpUnit library for parsing the HTML output from web applications. We'll see how this works in Chapter 7.

1.3.5 JUnitPerf

JUnitPerf, as you might expect, is a performance-testing tool built on top of JUnit. In Chapter 8, we show how to use JUnitPerf to ensure that unit tests execute within certain time limits and can handle expected usage loads. JUnitPerf does not help you find performance problems. Instead, it ensures that tests run within predefined performance limits.

You will often use JUnitPerf to complement commercial profiling tools. You may use a profiling tool to isolate performance bottlenecks in your code. Once you have fixed the bottleneck, you write a JUnitPerf test to ensure the code runs within acceptable time limits. The JUnitPerf test is then automated, and will fail if someone changes code and makes the code slow again. At this point, you probably go back to the profiling tool to locate the cause of the problem.

1.3.6 Application Servers

We round out our overview of tools with a brief mention of two open source server tools, JBoss and Tomcat. JBoss is a free application server supporting EJB, while Tomcat is a servlet and JSP container. The recipes in this book do not show how to use these tools in detail. Instead, we describe how to configure JBoss and Tomcat in order to support automated testing and continuous integration.

The kind of automation we are interested in occurs when you compile code and run tests. As mentioned earlier, you should strive for a simple command that compiles your code and then runs all of your tests. When working with an application server, typing a command like ant junit may actually do the following:

  1. Compile all code.

  2. Build a WAR file.

  3. Start Tomcat if it is not already running.

  4. Deploy the new WAR file.

  5. Run all unit tests, including those written using HttpUnit.

  6. Display a summary of the test results.

1.3.7 Setting Up a Build Server

At some point, your team will probably decide to create a build server. This is a shared machine that performs a clean build of the software on a continuous basis. The build server ensures that your application always compiles and that all tests run. The build server is easy to set up if you have been using CVS and Ant all along. For the most part, the build server operates exactly like each developer's PC. At various intervals throughout the day, the build server gets a clean copy of the code, builds the application, and runs the test suite.

Over time, however, you may want to make the build server more sophisticated. For instance, you might want the build server to monitor the CVS repository for changes. The build can start after some files have changed, but should not do so immediately. Instead, it should wait for a brief period of inactivity. The server can then get a clean copy of the sources using a timestamp from sometime during the inactive period. This process ensures that the build server is not getting code while programmers are committing changes to the repository.

You might also want to keep a change log of who changes what between each build, in order to notify the correct developers whenever the build succeeds or fails. We have found that notifying the entire team whenever a build fails is not a good idea because people begin to ignore the messages. With a change log, you can notify only those people who actually made changes. The developer process then begins to look like this:

  • Make a change, following all of the steps outlined earlier.[7]

    [7] In theory, the build shouldn't break if every developer follows the process before checking in code. We have found, however, that people occasionally "break the build" no matter how careful they are. An automated build server helps catch problems right away.

  • Commit changes to CVS.

  • Wait for an email from the build server indicating whether the build succeeds or fails.

There is a tool that does everything just described. It is called Cruise Control, and is available at http://cruisecontrol.sourceforge.net. Cruise Control works in conjunction with Ant, CVS, and JUnit to perform continuous builds on a server machine. The exact mechanics of setting up a build server vary widely depending on what version-control tool you use, whether you are using Linux or Windows, and what steps are required to build your particular application. The important thing to keep in mind is that builds should become a seamless part of everyday activity on an XP project, ensuring that developers can work without constantly stopping to figure out how to compile software and integrate changes with other team members.