Fabrizio Giudici is a Senior Java Architect with a long Java experience in the industrial field. He runs Tidalwave, his own consultancy company, and has contributed to Java success stories in a number of fields, including Formula One. Fabrizio often appears as a speaker at international Java conferences such as JavaOne and Devoxx and is member of JUG Milano and the NetBeans Dream Team. Fabrizio is a DZone MVB and is not an employee of DZone and has posted 67 posts at DZone. You can read more from them at their website. View Full User Profile

JUnit: A Little Beyond @Test, @Before, @After

08.26.2009
| 26814 views |
  • submit to reddit

A minor annoyance is that JUnit doesn't provide a readable output when running parameterized tests. In a report they are displayed as:

testImage[0] PASSED
testImage[1] PASSED
testImage[2] PASSED
testImage[3] PASSED
testImage[4] PASSED
testImage[5] FAILED
testImage[6] PASSED

This happens everywhere, both with JUnit run from a shell and with an IDE such as NetBeans. It isn't helpful to see that testImage[5] failed, as you have to manually look up the source code, or look at the test log files, to find out which test file triggered the failure.

Inspecting JUnit sources, I've found that there are two specific methods in Parameterized that returns the display name of the test. So I copied the source into my own MyParameterized that has been patched as follows:

public class MyParameterized extends Suite
{
private class TestClassRunnerForParameters extends BlockJUnit4ClassRunner
{
...

@Override
protected String getName()
{
return String.format("[%s]", Arrays.toString(fParameterList.get(fParameterSetNumber)));
}

@Override
protected String testName (final FrameworkMethod method)
{
return String.format("%s[%s]", method.getName(), Arrays.toString(fParameterList.get(fParameterSetNumber)));
}

...
}
}

In this way ExpectedResults.toString() is used as the test display name and I see speaking reports even in my NetBeans IDE:

testImage["http://www.rawsamples.ch/raws/nikon/d1x/RAW_NIKON_D1X.NEF"] PASSED 
testImage["http://www.rawsamples.ch/raws/nikon/d1/RAW_NIKON_D1.NEF"] PASSED
testImage["http://s179771984.onlinehome.us/RAWpository/images/nikon/D2X/1.01/_DSC0733.NEF"] PASSED
testImage["http://s179771984.onlinehome.us/RAWpository/images/nikon/D2Xs/1.00/DSC_1234.nef"] PASSED
testImage["http://www.rawsamples.ch/raws/nikon/d3/RAW_NIKON_D3.NEF"] PASSED
testImage["http://www.rawsamples.ch/raws/nikon/d3x/RAW_NIKON_D3X.NEF"] PASSED

I've not submitted a patch to JUnit yet since I'm studing this stuff to enhance it even more; for instance, to run tests in parallel. I've just rented a 8-core server for my Continuous Integration and I hope to make my tests run faster at least of a 4x/6x factor. But that's a topic for another article.

Published at DZone with permission of Fabrizio Giudici, author and DZone MVB.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Tags:

Comments

Guillaume Jeudy replied on Wed, 2009/08/26 - 12:41pm

Fabrizio,

 Have you tried testNG? It has a similar feature and seems easier to use than the example you posted here.

 See @DataProvider annotation. The test runs also display much nicer in eclipse IDE than what you posted in this article. It shows the test method with arguments passed.

 More over it has multi-threaded testing support though I haven't tried @DataProvider option combined with multi-threaded testing.

Anew Hope replied on Wed, 2009/08/26 - 8:43pm

@Fabrizio

One thing to note is that @RunWith is experimental in JUnit. I just checked the latest version of JUnit (4.7) and the javadoc is consistent with the following link:

http://junit.sourceforge.net/javadoc/org/junit/runner/RunWith.html

"...We added this feature late in development. While it seems powerful we expect the runner API to change as we learn how people really use it. Some of the classes that are currently internal will likely be refined and become public."

So I wouldn't rely on this for a large amount of testing. Also, the explanation of how JUnit finds constructors based on the number of arguments doesn't appear to be correct. I think a JUnit expects there to be only one constructor otherwise you'll get a "Testcase: initializationError" at least in JUnit 4 (standard in Netbeans 6.7.1).

I like your modification to Parameterized and hope they accept the changes.

Casper Bang replied on Thu, 2009/08/27 - 6:04am

Hmm, could one not also write a generator extension that hooks in front of JUnit's preprocessor? Then something like this would be possible:

    // @ImagesTest support 
    @interface JUnitGenerator{
        // Marker
    }
    @JUnitGenerator
    @interface ImagesTestGenerator{
        ImageTestParms[] value();
    }

    @interface ImageTestParms{
        String path();
        String md5();
        int w();
        int h();
    }
    
    // Test file
    @ImagesTestGenerator(
    {
        @ImageTestParms(path = "coke.jpg", md5 = "baa62439fb6e5183bd7c7be304dbf45f", w=1080, h=900),
        @ImageTestParms(path = "pepsi.jpg", md5="5e2c9f279cfed2de1260d71ff7b3d9c2", w=1600, h=1200),
        @ImageTestParms(path = "coffee.jpg", md5="efcf7c6bcfad3179c8876a8b184d0551",w=480, h=360)
    })
    public void ImagesTestTemplate(String path, String md5, int w, int h) {
        ImageReader ir = getImageReader(path);
        assertRaster(ir, md5);
        assertWidth(ir, w);
        assertHeight(ir, h);
    }

At compile time, let a simple AnnotationProcessor (recognizing the marker interface JUnitGenerator) generate the @Test methods via TreeTranslator. JUnit will see things as if you handcoded each and every test method.

-------------------------------------- 
All 3 tests passed.(0.066 s)
     ImageTest_coke passed (0.0 s)
     ImageTest_pepsi passed (0.0 s)
     ImageTest_coffee passed (0.0 s) 
-------------------------------------- 

 

I realize code generation is a touchy issue, but I always found it handy for testing (although admitedly  I've never done it exactly like this).

Ignacio Coloma replied on Thu, 2009/08/27 - 3:01am in response to: Anew Hope

@anewhope: the spring testing module relies on @RunWith, so hey better do not remove this in the short term :)

Denes Csepely replied on Fri, 2009/08/28 - 3:59am in response to: Ignacio Coloma

Yeah, Spring does and libs/frameworks provide their runwith classes too. If you need DI you use spring's runwith. If you need DI and want to leverage a runwith of any other framework u can't do that (or i missed something). You cannot specify two or more runwith therefore a workaround solution needed. I think the runwith is not as flexible as it would be.

Fabrizio Giudici replied on Fri, 2009/08/28 - 8:53am in response to: Denes Csepely

@gjeudy: Yes, I know about the existence of TestNG and that it offers more features than JUnit - in a way, one can say the latest JUnit versions are trying to catch up. The problem is that I'm already working with too many new libraries and products... trying to keep a limit at the moment. Probably I'll give it a try later in the year.

@anewhope. Yes, I know this thing is not part of a stable API. But it would not be hard to apply changes after JUnit eventually changes the API.

@casperbang. Very nice suggestion! No, I don't get worried by code generators :-) indeed there are already a couple in jrawio. Your suggestion sounds as a nice alternative to Groovy - indeed this brings me to a recurrent thought, that every time I think Groovy is useful for a problem, I later found (or somebody later suggests) an equivalent way to do things with Java ;-)

Peter Sellars replied on Sun, 2009/10/11 - 2:15am in response to: Fabrizio Giudici

Hi Fabrizio - did you submit a patch for this? Was it accepted or rejected? It seems useful to have a name for reference rather than just an index? Something like this could be an extended Parameterized class - such as IdentifedParamerterizedClass.

 Am curious as I am going to use your patch for an upcoming presentation on this feature of JUnit. Will acknowledge you in my presentation and link to this page. Am also going to look at using a file/or group of files to contain the test data.

john green green replied on Mon, 2009/10/26 - 3:27am

The problem is that I'm already working with too many new libraries nike shoes russia and products... trying to keep a limit at the moment. Probably I'll give it a try later in the year.

Mateo Gomez replied on Tue, 2012/04/17 - 12:28am in response to: Fabrizio Giudici

 hey Farbrizio, great job on this for answering the question back.Thanks!

 mexican dessert recipes

Matt Coleman replied on Fri, 2013/01/11 - 1:06am

great test...i so appreciate the before and after of it

buffalo search engine optimization 

Cata Nic replied on Tue, 2013/09/03 - 3:49am

 This post should be promotted into a better way. I think the solution presented here is easy to understand and should be promoted at large scale.

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.