I’ve been using and liking TestNG for a while now, but until recently hadn’t really appreciated everything it was giving me.
There are some software libraries/frameworks that feel like an implementation of an idea, and others that feel like an implmentation used to solve problems for every day development needs. I would say that JUnit/JSF fall into the former category, while projects like TestNG/tapestry fall into the latter. While the junit creators were busy wallowing in their own pools of XP brilliance, the developers of TestNG were busy solving real every day needs of developers.
I recently went through a fair amount of effort to upgrade the fairly extensive set of unit tests for tapestry to use TestNG and had some very surprising results. Not only did I get to to completely remove a lot of testing code put in place to fill in inadequacies of junit, the total runtime of the test cases were cut down. I mean cut way down, as in going from about 5 minutes to roughly 40-50 seconds. How could this be?
The biggest difference seems to be in how TestNG instantiates and runs your tests compared to JUnit. As an example, let’s look at a pseudo test:
public class MyTest { MyHeavyObject heavy; public void testFoo() { assert 2+2=5; } public void setUp() { heavy = new MyHeavyObject(); } public void testDoingThings() { heavy.doSomething(); assert heavey.hasDoneSomething(); } public void testBreakingHeavey() { try { heavy.wiggle(null); unreachable(); } catch (Exception e) { assert ex.getMessage().indexOf("Can't wiggle null objects.") > 0; } } }
On first glance things seem to more or less be ok with this test, but the major difference in implmentations will have a huge impact on performance if your heavey object is anything but the simplest pojo.
The reason for this is that JUnit will happily go in and re-instantiate your test class for each test method run. Yes, that’s right. Every single test method in there means a brand new instantiation of your test class and re-setup of said heavy object. Not what you expected is it? This is one major difference between the libraries.
Another great example of practical innovation is the @DataProvider annotation. On first read of the documentation it may not sink in right away what this means for your unit tests, but if you’ve ever had to iterate over test data and perform the same repetitive task over and over again you will start to see why this looks so simple and yet provides some pretty powerful functionality that will keep your unit tests simple/clean. Let’s say you’ve written a unit test that tests a query against a database. Something like this:
public class SqlTest { Collection _queries; public void setUp() throws Exception { Properties props = new Properties(); props.load(new FileInputStream("src/test-data/sqlqueries.properties")); _queries=props.values(); } public void testQueries() { Iterator it=_quieries.iterator(); while(it.hasNext()) { String query=(String)it.next(); performQueryOperation(query); } } public void performQueryOperation(String query){ // do stuff query... } }
Seems simple enough right? The only thing I don’t like is having to write a test method that doesn’t do the actual testing. “performQueryOperation” is the real test method in this class, but because I’d have no way of giving it data otherwise I have to write it like this. Now, let’s see what happens if we add in the @DataProvider logic from TestNG mentioned before:
public class SqlTest { Collection _queries; @Configuration(beforeTestClass = true) public void setUp() throws Exception { Properties props = new Properties(); props.load(new FileInputStream("src/test-data/sqlqueries.properties")); _queries=props.values(); } @DataProvider(name="sqldata") public Object[] createTestData() { return (Object[])_queries.toArray(new Object[_queries.size()]); } @Test(dataProvider=”sqldata”) public void performQueryOperation(String query){ // do stuff query… } }
Perhaps not the most compelling example in the world, but if you have any tests that do similar things by creating a JUnit suite with configured “pojo” sort of objects this kind of functionality starts to really cut down on redundant test code infrastructure logic.
I could probably go on and on with more examples but have to get back to work. Happy testing!
Latest Entries
- updated foxylicious extension for Firefox 3
- how to fix butt ugly ubuntu hardy heron fonts
- Where are our java leader’s opinions on closures?
- Tapestry 4.1.5 Released && various Tacos project releases (like Seam / jQuery support)
- painting by numbers, or how html cowards hide behind CSS
- Rails component envy
- OGNL 2.7.2 Released
- news flash - Swing is a terrible API to emulate in web development
- hip hop is neat
- good closures, bad closures
No Comments
Leave a Comment
trackback address