Chapter 5. JUnit and TAP

JUnit is the most famous unit testing framework for JAVA. It was created by Eric Gamma and Kent Beck.

With tap4j it is possible to generate TAP output for JUnit tests.

There are 2 possible ways to achieve the integration between tap4j and JUnit. One is creating a customized JUnit RunListener. The other way is using TestNG xml suites. In both cases it is necessary to add to the tests execution a Listener provided by tap4j.

Let's speak more about each possible solution.

So, imagine we have a simple JUnit test class and we want to make it generates TAP results.

Using a JUnit test executor class

Example 5.1. sample.MyJUnitTestClass.java

package sample;

import junit.framework.Assert;
import junit.framework.TestCase;

public class MyJUnitTestClass extends TestCase
{
	public void testEquals()
	{
		Assert.assertTrue( "O".equals( "0" ) );
	}
}

Here we have a sample test class called MyJUnitTestClass with 1 method testEquals supposed to return passed as result.

We need then to create the JUnit test executor class, where we must add the JUnit TAP reporter listener.

Example 5.2. sample.RunJUnitSuiteWithListener.java

package sample;

import org.junit.runner.JUnitCore;

import org.tap4j.ext.junit.JUnitTestTapReporter;

public class RunJUnitSuiteWithListener 
{
	public static void main(String[] args) 
	{
		// Create a JUnit suite
		TestSuite suite = new TestSuite();
		
		// Add every test class you want to the suite
		suite.addTestSuite(MyJUnitTestClass.class);
		
		// Instantiate a JUniteCore object
		JUnitCore core = new JUnitCore();
		
		// Add TAP Reporter Listener to the core object executor
		core.addListener(new JUnitTestTapReporter());
		
		// Run the test suite
		core.run(suite);
	}
}

So, when we execute the RunJUnitTestWithListener class, it will run the MyJUnitTestClass with the JUnitTestTapReporter listener. As a result we are going to have 3 tap files. 1 for the suite, 1 for the class 1 for the method.

Example 5.3. sample.MyJUnitTestClass-SUITE.tap

1..1
not ok 1 - sample.MyJUnitTestClass#testEquals
  ---
  message: JUnit 4.0 Test testEquals(sample.MyJUnitTestClass)
  severity: High
  source: sample.MyJUnitTestClass#testEquals
  datetime: '2011-05-20T11:04:11'
  file: sample.MyJUnitTestClass
  line: '10'
  name: testEquals
  error: 'null'
  backtrace: "junit.framework.AssertionFailedError: null\n\tat junit.framework.Assert.fail(Assert.java:47)\n\
    \tat junit.framework.Assert.assertTrue(Assert.java:20)\n\tat junit.framework.Assert.assertTrue(Assert.java:27)\n\
    \tat sample.MyJUnitTestClass.testEquals(MyJUnitTestClass.java:10)\n\tat sun.reflect.NativeMethodAccessorImpl.invoke0(Native\
    \ Method)\n\tat sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)\n\
    \tat sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)\n\
    \tat java.lang.reflect.Method.invoke(Method.java:597)\n\tat junit.framework.TestCase.runTest(TestCase.java:168)\n\
    \tat junit.framework.TestCase.runBare(TestCase.java:134)\n\tat junit.framework.TestResult$1.protect(TestResult.java:110)\n\
    \tat junit.framework.TestResult.runProtected(TestResult.java:128)\n\tat junit.framework.TestResult.run(TestResult.java:113)\n\
    \tat junit.framework.TestCase.run(TestCase.java:124)\n\tat junit.framework.TestSuite.runTest(TestSuite.java:243)\n\
    \tat junit.framework.TestSuite.run(TestSuite.java:238)\n\tat junit.framework.TestSuite.runTest(TestSuite.java:243)\n\
    \tat junit.framework.TestSuite.run(TestSuite.java:238)\n\tat org.junit.internal.runners.JUnit38ClassRunner.run(JUnit38ClassRunner.java:83)\n\
    \tat org.junit.runner.JUnitCore.run(JUnitCore.java:157)\n\tat org.junit.runner.JUnitCore.run(JUnitCore.java:145)\n\
    \tat sample.RunJUnitSuiteWithListener.main(RunJUnitSuiteWithListener.java:56)\n"
  ...

Example 5.4. sample.MyJUnitTestClass.tap

1..1
not ok 1 - sample.MyJUnitTestClass#testEquals
  ---
  message: JUnit 4.0 Test testEquals(sample.MyJUnitTestClass)
  severity: High
  source: sample.MyJUnitTestClass#testEquals
  datetime: '2011-05-20T11:04:11'
  file: sample.MyJUnitTestClass
  line: '10'
  name: testEquals
  error: 'null'
  backtrace: "junit.framework.AssertionFailedError: null\n\tat junit.framework.Assert.fail(Assert.java:47)\n\
    \tat junit.framework.Assert.assertTrue(Assert.java:20)\n\tat junit.framework.Assert.assertTrue(Assert.java:27)\n\
    \tat sample.MyJUnitTestClass.testEquals(MyJUnitTestClass.java:10)\n\tat sun.reflect.NativeMethodAccessorImpl.invoke0(Native\
    \ Method)\n\tat sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)\n\
    \tat sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)\n\
    \tat java.lang.reflect.Method.invoke(Method.java:597)\n\tat junit.framework.TestCase.runTest(TestCase.java:168)\n\
    \tat junit.framework.TestCase.runBare(TestCase.java:134)\n\tat junit.framework.TestResult$1.protect(TestResult.java:110)\n\
    \tat junit.framework.TestResult.runProtected(TestResult.java:128)\n\tat junit.framework.TestResult.run(TestResult.java:113)\n\
    \tat junit.framework.TestCase.run(TestCase.java:124)\n\tat junit.framework.TestSuite.runTest(TestSuite.java:243)\n\
    \tat junit.framework.TestSuite.run(TestSuite.java:238)\n\tat junit.framework.TestSuite.runTest(TestSuite.java:243)\n\
    \tat junit.framework.TestSuite.run(TestSuite.java:238)\n\tat org.junit.internal.runners.JUnit38ClassRunner.run(JUnit38ClassRunner.java:83)\n\
    \tat org.junit.runner.JUnitCore.run(JUnitCore.java:157)\n\tat org.junit.runner.JUnitCore.run(JUnitCore.java:145)\n\
    \tat sample.RunJUnitSuiteWithListener.main(RunJUnitSuiteWithListener.java:56)\n"
  ...

Example 5.5. sample.MyJUnitTestClass#testEquals.tap

1..1
not ok 1 - sample.MyJUnitTestClass#testEquals
  ---
  message: JUnit 4.0 Test testEquals(sample.MyJUnitTestClass)
  severity: High
  source: sample.MyJUnitTestClass#testEquals
  datetime: '2011-05-20T11:04:11'
  file: sample.MyJUnitTestClass
  line: '10'
  name: testEquals
  error: 'null'
  backtrace: "junit.framework.AssertionFailedError: null\n\tat junit.framework.Assert.fail(Assert.java:47)\n\
    \tat junit.framework.Assert.assertTrue(Assert.java:20)\n\tat junit.framework.Assert.assertTrue(Assert.java:27)\n\
    \tat sample.MyJUnitTestClass.testEquals(MyJUnitTestClass.java:10)\n\tat sun.reflect.NativeMethodAccessorImpl.invoke0(Native\
    \ Method)\n\tat sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)\n\
    \tat sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)\n\
    \tat java.lang.reflect.Method.invoke(Method.java:597)\n\tat junit.framework.TestCase.runTest(TestCase.java:168)\n\
    \tat junit.framework.TestCase.runBare(TestCase.java:134)\n\tat junit.framework.TestResult$1.protect(TestResult.java:110)\n\
    \tat junit.framework.TestResult.runProtected(TestResult.java:128)\n\tat junit.framework.TestResult.run(TestResult.java:113)\n\
    \tat junit.framework.TestCase.run(TestCase.java:124)\n\tat junit.framework.TestSuite.runTest(TestSuite.java:243)\n\
    \tat junit.framework.TestSuite.run(TestSuite.java:238)\n\tat junit.framework.TestSuite.runTest(TestSuite.java:243)\n\
    \tat junit.framework.TestSuite.run(TestSuite.java:238)\n\tat org.junit.internal.runners.JUnit38ClassRunner.run(JUnit38ClassRunner.java:83)\n\
    \tat org.junit.runner.JUnitCore.run(JUnitCore.java:157)\n\tat org.junit.runner.JUnitCore.run(JUnitCore.java:145)\n\
    \tat sample.RunJUnitSuiteWithListener.main(RunJUnitSuiteWithListener.java:56)\n"
  ...

As we have only one test suite with one test class and only one test method, all 3 files have exactly the same content about tests.

Pay attention! When you run the executor class it is not going to show the results on JUnit eclipse view. It runs in background and generates the TAP files in the project root. You need to refresh your project on navigator view to see the tap files that were generated

Finally the command line for execution of the class is

Example 5.6. 

java -classpath %CLASSPATH%;junit-4.0.jar;jcommander-1.5.jar;commons-io-1.4.jar;
guice-2.0.jar;commons-lang-2.5.jar;bsh-2.0b4.jar;snakeyaml-1.7.jar;
aopalliance-1.0.jar;tap4j-1.4.3.jar;. RunJUnitTestWithListenerClass

Using TestNG suite xml files to execute your JUnit tests

This is strange, but it is a very good idea. It is easy to implement and works perfectly as TestNG can interpret JUnit and run just like TestNG. Here, you only need to create xml suite files with the tap4j listeners, just like you would do if the tests were made with TestNG. The only difference is that you need to set the "junit" attribute inside "<suite<" branch to "true". Really easy no? TestNG takes care of the rest.

So your suite xml file will look like this:

Example 5.7. sample.MyJUnitTestClass#testEquals.tap

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="JUnit tests with TestNG suite" verbose="0" parallel="false" junit="true">
  <listeners>
    <listener class-name="org.tap4j.ext.testng.SuiteTAPReporter" /> 
    <listener class-name="org.tap4j.ext.testng.TestTAPReporter" /> 
  </listeners>
  <test name="JUnit tests with TestNG suite" preserve-order="false">
    <classes>
      <class name="sample.MyJUnitTestClass"/>
    </classes>
  </test>
</suite> 


The results are going to be given just like in the other way.