Ant and multiple JUnit test cases

In this Ant tutorial installment, we’ll use the JUnit task to execute multiple test classes. Our starting point will be the trivial JUnit task presented previously. This example was developed using version 1.5.1 of Ant and version 3.8.1 of JUnit.

For simplicity, we’ll add the additional JUnit test class shown below. This class uses the assertEquals method with arguments that will always cause an AssertionFailedError. Please place this file in a sub-directory named test as before.

1
2
3
4
5
6
7
import junit.framework.*;
public class Test extends TestCase {
public void test()
{

assertEquals( "Equality Test", 0, 1 );
}
}

And here is the build.xml file that we’ll be using. There are just two changes from our previous example (shown in bold). First, we set property TALK to false in order to decrease the amount of output. And second, we replace the single test sub-element of the JUnit task with a batchtest that uses a file system regular expression to identify the test classes to run.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
<project default="all">
<property name="tst-dir" location="test" />
<property name="TALK" value="false" />

<path id="classpath.base">
</path>

<path id="classpath.test">
<pathelement location="/Projects/Java/Lib/junit.jar" />
<pathelement location="${tst-dir}" />
<path refid="classpath.base" />
</path>

<target name="compile-test">
<javac srcdir="${tst-dir}"
verbose="${TALK}"
>

<classpath refid="classpath.test"/>
</javac>
</target>
<target name="clean-compile-test">
<delete verbose="${TALK}">
<fileset dir="${tst-dir}" includes="**/*.class" />
</delete>
</target>

<target name="test" depends="compile-test">
<junit>
<classpath refid="classpath.test" />

<formatter type="brief" usefile="false" />
<batchtest>
<fileset dir="${tst-dir}" includes="**/Test*.class" />
</batchtest>
</junit>
</target>

<target name="all" depends="test" />

<target name="clean" depends="clean-compile-test" />
</project>

We can now execute our test.

1
$ ant clean
Buildfile: build.xml

clean-compile-test:
   [delete] Deleting 2 files from /Tutorial/Ant/JUnit2/test

clean:


BUILD SUCCESSFUL
Total time: 4 seconds
$ ant test
Buildfile: build.xml

compile-test:
    [javac] Compiling 2 source files

test:
    [junit] Testsuite: Test
    [junit] Tests run: 1, Failures: 1, Errors: 0, Time elapsed: 0.012 sec

    [junit] Testcase: test(Test):	FAILED
    [junit] Equality Test expected:<0> but was:<1>
    [junit] junit.framework.AssertionFailedError: Equality Test expected:<0> but was:<1>
    [junit] 	at Test.test(Unknown Source)


    [junit] TEST Test FAILED
    [junit] Testsuite: TestExample
    [junit] Tests run: 1, Failures: 0, Errors: 0, Time elapsed: 0.001 sec


BUILD SUCCESSFUL
Total time: 5 seconds

Note that ant exits with BUILD SUCCESSFUL despite a junit failure. This was an intentional choice by the designers to allow the completion of a test suite. A revised target that includes the recommended JUnit task structure is shown below. In this structure, a property is set when the JUnit task encounters a failure (test failure or JUnit error). After the JUnit task completes, we fail the target if the failureProperty was set.

1
2
3
4
5
6
7
8
9
10
11
<target name="test" depends="compile-test">
<junit failureProperty="test.failure" >
<classpath refid="classpath.test" />
<formatter type="brief" usefile="false" />
<batchtest>
<fileset dir="${tst-dir}" includes="**/Test*.class" />
</batchtest>
</junit>

<fail message="test failed" if="test.failure" />
</target>

We’ll also modify the compile-test target by setting debug=”true” in the javac task as shown. This will add line number information to the errors reported from JUnit.

1
2
3
4
5
6
7
8
<target name="compile-test">
<javac srcdir="${tst-dir}"
verbose="${TALK}"
debug="true"
>

<classpath refid="classpath.test"/>
</javac>
</target>

Now we execute ant:

1
$ ant clean
Buildfile: build.xml

clean-compile-test:
   [delete] Deleting 2 files from /Tutorial/Ant/JUnit2/test

clean:

BUILD SUCCESSFUL
Total time: 3 seconds
$ ant test
Buildfile: build.xml

compile-test:
    [javac] Compiling 2 source files

test:
    [junit] Testsuite: Test
    [junit] Tests run: 1, Failures: 1, Errors: 0, Time elapsed: 0.009 sec

    [junit] Testcase: test(Test):	FAILED
    [junit] Equality Test expected:<0> but was:<1>
    [junit] junit.framework.AssertionFailedError: Equality Test expected:<0> but was:<1>
    [junit] 	at Test.test(Test.java:7)


    [junit] TEST Test FAILED
    [junit] Testsuite: TestExample
    [junit] Tests run: 1, Failures: 0, Errors: 0, Time elapsed: 0 sec


BUILD FAILED
file:/Tutorial/Ant/JUnit2/build.xml:36: test failed

Total time: 4 seconds

Much better. Here’s the full build.xml file:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
<project default="all">
<property name="tst-dir" value="test" />
<property name="TALK" value="false" />

<path id="classpath.base">
</path>

<path id="classpath.test">
<pathelement location="/Projects/Java/Lib/junit.jar" />
<pathelement location="${tst-dir}" />
<path refid="classpath.base" />
</path>

<target name="compile-test">
<javac srcdir="${tst-dir}"
verbose="${TALK}"
debug="true"
>

<classpath refid="classpath.test"/>
</javac>
</target>
<target name="clean-compile-test">
<delete verbose="${TALK}">
<fileset dir="${tst-dir}" includes="**/*.class" />
</delete>
</target>

<target name="test" depends="compile-test">
<junit failureProperty="test.failure">
<classpath refid="classpath.test" />

<formatter type="brief" usefile="false" />
<batchtest>
<fileset dir="${tst-dir}" includes="**/Test*.class" />
</batchtest>
</junit>

<fail message="test failed" if="test.failure" />
</target>

<target name="all" depends="test" />
<target name="clean" depends="clean-compile-test" />
</project>

Disclaimer: I don’t claim to be an expert on ant. Please send comments and corrections.

Kiwi bad luck Continues

Team New Zealand’s run of bad luck continues in the America’s Cup. Down 4-0, TNZ needs to win 5 in a row to retain the cup. I’m pulling for them, but I think that I’ve heard the fat lady warming up for days.

While I’m sorry for the New Zealanders, I’m also sorry for the America’s Cup competition. In the past, the national guidelines were an artifice by which the New York Yacht Club kept American technology away from the competition. But Australia’s historical win in 1983 demonstrated that sailing technology ignores national boundaries. And I think that it will be a shame if the Swiss challenger Alinghi wins with a brain trust hired away from TNZ.

I rooted for America in the challenger round, not Oracle, not BMW, and not Larry Ellison. And I think that national loyalty is vital to the competition. If TNZ wins, then I would expect the rules to change to reflect a greater respect for national boundaries. I doubt that Alinghi would accept such changes, as land locked Switzerland is hardly a hotbed of sailing competition

Don't do it if you don't mean it

Scoble has posted his Corporate Weblog Manifesto. It may seem like it goes without saying, but my experience is that it is almost always worth saying: Rule 0: Don’t do it if you don’t mean it.

There are always going to be reasons not to post. Someone in marketing will be bent out of shape. Someone in sales will be blaming you for a lost deal. The alligators will be up to your armpits. But you still need to soldier on. Several items of the Manifesto address the need to be fast. You need to make it a priority.

Is the honeymoon over?

Is the honeymoon over for Google™? I hadn’t given the issue much thought until I read this article in Wired a few months back. Now, I’m beginning to think that Wired had remarkable timing on the subject.

Google™ is generally perceived to be one of the good guys. It reliably finds what we’re looking for. It accepts advertising, but it doesn’t try to trick us when the ads are presented. As an employer, it remains a company that thinks work should be fun and whose employees are able to think that they can change the world.

But Google™ is heavily dependent upon good will. It’s technology is good, but probably not that much better than the competition. It’s the dominant search site, but we’ve all had enough experience with monopolies (benign or otherwise) to distrust single source anything. It is the best, because we’ve placed our trust in it and we allow it to be the best.

It seems to me that trust is eroding. The letter to wordspy is the quite possibly the mildest legal correspondence that I’ve ever read. Some perceive this to be bullying, others perceive it to be lawyers behaving badly. A year or two back, I think Google™ would have been given more slack.

Unintentional Abandonment

I’m not a trademark lawyer, but I think Google™ pretty much has to behave this way (via Dave Winer). I believe that there is a gotcha in US trademark law by which you can lose your trademark by unintentional abandonment. One of those ways seems to be if the term becomes generic.

5pm It’s not really being a bully as much as it is them trying to protect their identity. Similarly, property owners shouldn’t let random people cut across their property as a shortcut. I believe that under the right [wrong?] circumstances the shortcut can become a permanent public easement.

6pm Hmmm, Cory Doctorow believes that the standard for abandonment is much higher:

Allowing the generic use of “to google” by critical/academic sites like WordSpy does not constitute an abandonment of trademark (in fact, trademark abandonment involves a very high standard that is far in excess of allowing people to casually use your mark).

I’m just a bit sceptical about the parenthesized comment. Suppose google became a commonly used verb that meant “to search online” (not “to use Google™ search”). Then suppose that a website re-labeled all their search buttons to read “google”. What recourse would Google™ have?

Taking the Quiz

I just took the Java Development with Ant Quiz. The result revealed a couple issues in my Ant tutorial pages that are now corrected. First, the location attribute should be used instead of the value attribute when defining a property location This binds the property to a fixed filesystem location rather than a string in case it is passed to another process. And second, the java task should use fork=”true” to provide full access to the Java runtime classes and to prevent a call to System.exit() from terminating Ant’s runtime.

What is a Blog Anyway?

The latest round of blog introspection has started (see Jeff Walsh and Glenn Fleishman). Personally, I think that the comparison of blogs to journalism is premature. Journalism is a fairly mature profession, with accepted forms and rules of conduct. Weblogs are nothing like that.

I think that it is important to realize that weblogs are very close to their evolutionary start and are still experiencing a tremendous amount of diversity. In 5 or 10 years, we’ll be able to look at the survivors and determine the dominant weblog forms. In addition, weblogs are primarily the work of a single author and in many cases that author is still developing a voice. We can expect to see blogs change as their authors change.

In contrast, I think that journalism is a profession. The institutions have their own institutional voice defined by their editorial staff. Individuals have honed their voice through years of service to the institutions. In either case, they produce a consistent product.

So don’t worry about what weblogs are or aren’t. Instead, spend some time thinking about what your weblog should be.

That was an Adventure

When I started this weblog, I expected to talk about Content Management and Software Development. As events have unfolded, this blog has been about Sports, Content Management and Software Development. So I added some new categories, wrestled with Radio for a few hours, and now my Sports and Software categories are a going concern.

What is the College Experience?

It is always brought up as a reason for high school athletes to go to college instead of the pros, but I don’t recall any discussions of what it is. To me, the college experience is a late night tunnel tour, tossing pennies to Tecumseh for good luck, senior ditch day at Caltech, a kiss on the Stanford quad under an October full moon, and igloo bridge after the blizzard of 78.

Bringing it back to sports, it’s a bonfire the night before the big game, and it’s Ralph Sampson walking outside to a shower from his room on the lawn. I doubt that it has anything to do with two-a-days in August. What memories make up the college experience for you? Unless you were a participant, I doubt that any of them have anything to do with the quality of your football or basketball teams.

Paying college athletes may come in under the guise of rewarding the athletes, but to me it has the bad taste of justifying even more sacrifice from the athlete. The football and basketball teams already spend too much time in their own world separated from the general student population.

Community is build upon shared experience, and I want to see the athletes and the students share more, not less. I doubt that Ralph Sampson would have been chosen for a room on the lawn if he was paid.

Make them Blog

You can lead some people to Pyra, but you can’t make them blog. Google Search solves half the problem, but you still have to create the content. It turns out that Google may have an answer here as well - because we are dealing with employees, not the general population. And we can easily reward employees who author content with a high page rank. Remember that the discovery of hidden gems can be just as valuable as the creation of new ones, so reward those who link as well.