Based on my experience with both frameworks, testng has a few convenient features that the JUnit team has declined to implement for several years. I prefer it to JUnit for this reason. Converting to testng is easy since it basically supports pretty much everything in JUnit (there's a converter plugin for eclipse even), converting back to JUnit is not so great because of these missing features.
In testng @BeforeClass
methods are not static and execute before the tests in that class run rather than when the test class is loaded (the JUnit behavior). I once had a JUnit project where all of the database tests (a few dozen) initialized the database right at the start, which was quite idiotic behavior. There was a lot of debate for and against this in the JUnit community. The gist of it was that every test method should have its own test fixture and therefore you should not have a beforeAll style method that is non static because that would allow you to sneakily set an instance variable once and then use it in all your tests. Valid, but really annoying for integration tests. TestNG gives users the choice here. Junit does not, by design, which is annoying.
Testng data providers are a bit more flexible than the JUnit equivalent. You can specify per test which data provider method should be providing the input instead of having a one size fits all approach for the entire class as in JUnit. So you can have positive and negative case data providers for your tests in one class. Very nice to have.
You can mark up a class with @Test
in testng, which means: every public method is a test. In Junit you need to copy/paste @Test
on every method.
An annoyance with both is the way hamcrest is bundled with JUnit and the way JUnit is bundled with testng. There are alterate jars in maven that don't have this issue.
My big worry with both frameworks is that they both seem to have stopped evolving. Releases are increasingly less frequent and tend to have less and less noteworthy features. The whole BDD movement seems to have had little impact on either framework for example. Also, JUnit could simply have adopted most of what I listed above. There is no good technical reason why JUnit can't implement any of these things; the people behind JUnit just choose not to implement these things. Both projects seem to be lacking a vision for future directions as well and they seem to be happy doing just minor tweaks for the last few years.