Welcome!

Richard Cariens

Subscribe to Richard Cariens: eMailAlertsEmail Alerts
Get Richard Cariens via: homepageHomepage mobileMobile rssRSS facebookFacebook twitterTwitter linkedinLinkedIn


Related Topics: RIA Developer's Journal, Java Developer Magazine

RIA & Ajax: Article

Write Right Java Faster Using Test-Driven Development

The benefits of embracing TDD

Testing Java code is increasingly a task taken on by developers rather than separate teams to which the programs are handed. Many Java developers are now familiar with JUnit and know the different between unit tests and integration tests. This has been driven largely by the focus on test-driven development (TDD) in extreme programming (XP) and other agile software development methodologies. While the industry-at-large has recognized the value of unit tests and has a new outlook on testing in general, for the most part, actual TDD (meaning, the tests are written first) is not usually practiced outside of hardcore agile shops.

In this article, we'll present a specific example (based on a real-world scenario that we recently faced) and walk step-by-step how to take a pure TDD approach and hopefully show the benefits of embracing TDD completely in this scenario. (For a clear and concise explanation of some of the major benefits of TDD in general, see www.extremeprogramming.org/rules/testfirst.html.)

The Scenario
This scenario is modeled closely on one we faced at a client site recently. In short, we were a pair on a development team working on a project with typical issues:

  1. A deadline/delivery date had been set
  2. Little or no requirements existed and
  3. It didn't look like we'd be getting requirements any time soon (due to limited staffing, etc.).
The project goal was to build a marketing Web site around the client's existing feed management product. At a high level and besides product marketing, the Web site should include basic information and some rudimentary services related to Web feeds (RSS, Atom, etc.). The list of services included:
  1. A "feed finder" service: the user must be able to enter a URL somewhere on the site that will produce a list of candidate feed URLs that it found at that URL.
  2. A "feed validation" service: the site will analyze a user-provided URL and inform the user if the document found at that URL is a valid RSS or Atom feed.
To mimic the last-minute changes in requirements we'll imagine that a business stakeholder stopped by our cubicles and provided some additional information this morning: the site must have a "coolness" factor, i.e., make the site an active Web 2.0/AJAX site. A new person has been hired to handle the user interface, HTML and JavaScript; our job is to build remote components that will implement the services listed above.

Constraints
The client's standard production platform is Java 5, JBoss 4.x, and MySQL 4.1 on Red Hat Linux. We're supplied with a workstation running Windows XP Pro, Eclipse 3.1, Java 5, and JBoss 4.0.1.

Decisions
We decided to assess the risk level of each service. RSS and Atom standards are well known and there are a variety of tools that we can probably use to implement the "feed validation" service, which doesn't feel that risky. The "feed finder" service feels much riskier since there are many ways to detect a candidate feed for a supplied URL, some of which are:

  1. We can try to discover the feed from the HTML document at the supplied URL using <link> elements in the HTML <head> section;
  2. We can spider the URL's Web site for common feed file names like rss.xml, atom.xml, rdf.xml, feed.rss, etc.;
  3. We can try to get the feed URL by using a Web Service like Syndic8's XML-RPC services.
We're not too worried about the service interfaces with the AJAX pages; we know we'll probably have to write a servlet that accepts a GET or POST request with a URL parameter. And while we're not sure if we'll use XML or plain text in the response, we think that's a straightforward problem. We proceed to tackle what we think are the risky unknowns (like how to find feed references from a document) and start by writing a test for the discovery method of "feed finding."

Getting Started
We think it will be easier to write the tests if we break up the service into two discrete steps:

  1. Download the HTML document from the supplied URL;
  2. Parse/search the document for <link> tags with "type" attributes of "application/rss+xml," "application/atom+xml," "application/x.atom+xml," or "application/x-atom+xml."
First we create a simple empty Eclipse project for our feed finder and add a new JUnit Test Case (see Figure 1). Then we call the test "FeedAutoDiscovererUnit".

For starters, we add a test for detecting RSS links that automatically fails (since we don't have anything to test yet) and we're ready to start defining success and failure criteria.

Note that even though we haven't written any application code, we know that we're probably headed towards creating a class called FeedAutoDiscoverer and that we're expecting this class to be able to find an RSS link in a document.

Writing the Test
Now we have to generate some input HTML that contains a <link> tag with a type of "application/rss+xml" (the Atom test will come later). We add a simple in-line HTML document that contains the expected link to our "testFindsRssLink" method.

Now we're ready to introduce the component that will implement the discovery logic. We replace the "fail" statement with a call to an instance of a class called FeedAutoDiscoverer. We decide that this class should implement a method called "discoverLinks" that accepts a string and returns a list of strings. We also add assertions that help us know if the FeedAutoDiscoverer discovered the RSS link type correctly:

  1. We know that our test input only has one <link> tag in it so we assert that the FeedAutoDiscoverer returns a list with one element;
  2. We know that our test input contains a <link> tag with a specific "href" attribute and we want to see that expected href value in the list (see Figure 2).
Note the red decorations on the test source. The FeedAutoDiscoverer class doesn't exist yet so let's use Eclipse's "quick-fix" capabilities to create it for us from this test (press Ctrl + 1 while the red-underlined code has cursor focus).

We tell Eclipse we want this class to live under the "src" source folder instead of the "tests" folder and then let the wizard generate the class for us.

Once the empty FeedAutoDiscoverer is generated, we still find we can't compile our test because the method "discoverLinks" method doesn't exist. We let Eclipse generate this for us as well (see Figure 3).

Eclipse generates an empty method for us with a handy "TODO" reminder that we'll eventually have to change this method.

The test will now compile and is ready for its first run. Of course it will fail, but that's to be expected since our FeedAutoDiscoverer just returns null.

Now that we've written the test, we can focus on making the test pass.

Making the Test Pass
We're going to try to write the simplest code that will make the test pass. We know there are several ways to detect a <link> tag in an HTML document:

  1. We can sub-class the HTMLEditorKit.ParserCallback from the javax.swing.text.html package;
  2. We could use a third-party HTML/XML parsing library like TagSoup, HotSax, NekoHTML, JTidy, etc;
  3. We could use regular expressions and other "brute force" techniques.

More Stories By John Evans

John Evans is the founder and president of JPEvans, Inc. (www.jpevans.com), a small independent computer consulting company based in Northern Virginia just outside of Washington D.C. John has over 10 years of professional experience in software development. He has successfully developed and deployed large-scale software systems for several large multi-national corporations.

More Stories By Richard Cariens

Richard Cariens is an independent software consultant in the Washington D.C. area (www.jpevans.com). He has over 10 years of experience testing, developing, designing, and architecting Internet technology and financial systems. Rich holds an MS in computer science from George Mason University in Fairfax, Virginia.

Comments (4) View Comments

Share your thoughts on this story.

Add your comment
You must be signed in to add a comment. Sign-in | Register

In accordance with our Comment Policy, we encourage comments that are on topic, relevant and to-the-point. We will remove comments that include profanity, personal attacks, racial slurs, threats of violence, or other inappropriate material that violates our Terms and Conditions, and will block users who make repeated violations. We ask all readers to expect diversity of opinion and to treat one another with dignity and respect.


Most Recent Comments
The Cherbin 08/22/06 06:35:33 PM EDT

The best way to write right java is either
1. DONT use it at all
2. If you are forced to use it, use java to execute an external application, and write that application in another language.

JDJ News Desk 07/30/06 11:36:06 AM EDT

Testing Java code is increasingly a task taken on by developers rather than separate teams to which the programs are handed. Many Java developers are now familiar with JUnit and know the different between unit tests and integration tests. This has been driven largely by the focus on test-driven development (TDD) in extreme programming (XP) and other agile software development methodologies. While the industry-at-large has recognized the value of unit tests and has a new outlook on testing in general, for the most part, actual TDD (meaning, the tests are written first) is not usually practiced outside of hardcore agile shops.

JDJ News Desk 07/30/06 08:56:13 AM EDT

Testing Java code is increasingly a task taken on by developers rather than separate teams to which the programs are handed. Many Java developers are now familiar with JUnit and know the different between unit tests and integration tests. This has been driven largely by the focus on test-driven development (TDD) in extreme programming (XP) and other agile software development methodologies. While the industry-at-large has recognized the value of unit tests and has a new outlook on testing in general, for the most part, actual TDD (meaning, the tests are written first) is not usually practiced outside of hardcore agile shops.

Jon Log 07/26/06 05:22:53 AM EDT

Though the development in question is a trivial one, your conclusions encapsulate perfectly why TDD is a poor substitute for a proper development model:

Conclusion 1: "It forced us to translate our ambiguous requirements into verifiable test criteria"

But on whose terms ? The "us" in your statement implies that you mean the developer. This is WRONG. An ambiguity must be flattened out by the specifier and agreed on by the developer. This is the contract you have with whoever you are providing a solution for. A developer is just as likely to interpret an ambiguity incorrectly as correctly.

Conclusion 2: "The test criteria helped us focus on doing just what was needed to pass the test, and thus hopefully satisfying the requirements"

Well, "hopefully" is not good enough. The objective of the developer MUST be to make their code do what it should in relation to the requirement. By encouraging a coder to produce something that meets only the requirements of a test that they themselves have defined is obviously a corrupt concept. Focussing on the test rather than the true objective of the requirement breaks a valuable chain of responsibility that should exist at every stage of the process.

Conclusion 3: "We avoided the heavy front-loading of design documentation and focused on getting some working code."

This is the icing on the cake. Spending time thinking about and documenting a design delivers too many obvious benefits to mention. However, in relation to the anti-patterns that I believe are a core part of your conclusions, designing a solution before implementing it would force any ambiguities to be resolved at the correct time and in the correct manner (against a coherent definition that is intelligible to the business and development communities). In addition, assuming that a solution involves more that one method (which of course it generally does) it would promote the production of a more appropriate test path with the requirements taking precedence over the tests themselves.