I have to start this blog with a rant. I am currently working at Hibernate's reference implementation of JSR 303. I started out using Junit 4 for unit tests. However, in order to leverage from some other framework I had to switch to TestNG. I thought it should be quite easy to change from one framework to the other - basically just switch the import statements. Turns out is was more difficult than that. After a global find and replace in the test files replacing the package statement I ended up with multiple compilation errors. The problem was that in Junit the assertEquals method has the signature assertEquals(String message, Object expected, Object actual) whereas TestNG's assertEquals is assertEquals(Object expected, Object actual, String message). WTF! Given that TestNG came later - which reason did they have to swap the arguments? Just to make it harder to migrate from one to the other? This applies of course for all assert statements. I am just using assertEquals as an exmaple.

Anyways, I started manually changing the calls, but that was just too tedious. It was time to get sed out again, but soon I realized that sed was not going to cut it this time, because there is no way do non greedy regular expressions . The expression also had to take care of single and multi line statements. An example for a multi line statement would be:

"Wrong propertyPath",

Time to get good old perl out again. Here is what I came up with (the whole expression is one line!):

perl -pi -0777 -e 's/(assertEquals.|\n*?)(".*?"),[ ,\n,\t]*(\w*?,)([ ,\n,\t]*.*?)([ ,\n,\t]*)\);/$1$3$4,$5$2\ );/g' MyClass.java

Well, it did not solve all my problems. In some cases the formatting was a little off, but that could be easily fixed with my IDE. Some comments about the expression:
  • the -i option allows editing of the file in-line. A very nice feature of perl and something I miss a lot in sed
  • -0777 specified the line break character in octa-decimal. Assuming 777 does not appear in your file this is an easy way to match across lines
  • there are a lot of \n matches. Even when matching across the whole file .* won't match the new line character. It has to be explicitly specified
  • using regular expression buffers I am able to capture the groups of the statement an put the expression together in a different order using $x notation for referring to the matching buffers
  • and I almost forgot the ? after the operators ?, + and * turns on non greedy machting
Admittedly, I could have almost changed all statements manually in the time I figured out the regular expression, but this solution was ways more satisfactory :)


Old comments

You could also have used the AssertJUnit class, which is a copy of JUnit's class, so it's just a one line change per file...
2009-04-05Fredrik Rubensson
WTF indeed! The whole Java thing seems to be a complexity conspiracy. (Don't get me started on portlets....)
2009-04-05Hardy Ferentschik
Right. I wish the TestNG documentation would have made AssertJunit more visible. Not only a little node at the end of the documentation. Or what's about adding a comment to the migration guide? What can we learn from this? There are many ways to skin a cat! And of course there is a big Java complexity conspiracy going on ;-)
Good point about the migration guide, I updated it to reflect this.

2009-04-05Hardy Ferentschik
Thanks to you :)