Reply to comment
TestNG - parallel test execution - example - unique ID generator
Submitted by Tomek Kaczanowski on Tue, 05/12/2009 - 21:16Today I played with unique IDs generator. While doing this, I have found how very useful is one of the features of TestNG framework - its ability to run tests in parallel.
Please notice the source code attached at the end of this text.
Some info
At the time of writing this post the latest version of TestNG is 5.9, and for jUnit it is 4.6.
At the end of this text you'll find source code. See the readme.txt file and read javadocs.
First version
Somewhere in project X I encountered a code like this:
public interface IdGenerator {
Long nextId();
}
public class NaiveIdGenerator implements IdGenerator {
// TODO change it
public Long nextId() {
return System.currentTimeMillis();
}
}
There were no real tests for this piece of code (grrrr.....) which made me very suspicious, so I decided to take a closer look.
The other reason for my interest in this snippet of code was, that I knew that System.currentTimeMillis can't be really trusted for such a job (see here or search the web for more info).
My first test looked like this:
@Test
public class NaiveIdGeneratorTest {
private IdGenerator idGen = new NaiveIdGenerator();
public void idsAreUnique() {
Long idA = idGen.nextId();
Long idB = idGen.nextId();
assert !idA.equals(idB);
}
}
The test FAILED. Which means, the NaiveIdGenerator is flawed. :(
Second version
Ok, it's time to make the test PASS. Here comes the second attempt:
public class BetterNaiveIdGenerator implements IdGenerator {
private static Long nextId = System.currentTimeMillis();
public Long nextId() {
return nextId++;
}
}
Is it any better ? The test described above PASSED, so it is better, isn't it ?
Second test
Well, not really. It's still working only "sometimes". Why ? Because ++ operations are not atomic ! Apparently there are "more atomic" than previous version, because this test sometimes will pass, but it's not enough for the unique IDs generator, right ?
To reveal the bug, I used a nice feature of TestNG framework - running tests in parallel.
public class BetterNaiveIdGeneratorParallelTest {
private IdGenerator idGen = new BetterNaiveIdGenerator();
private Set<Long> ids = new HashSet<Long>(100);
@Test(threadPoolSize = 7, invocationCount = 100)
public void idsAreUnique() {
assertTrue(ids.add(idGen.nextId()));
}
}
Important - this test will sometimes PASS and sometimes will FAIL (at least that what happens on my machine).
As you can see I use Set API to check if generated ID is unique.
Third version
Finally, I came to the following solution (I copied & pasted it from somewhere, to be honest):
public class JVMUniqueIdGenerator implements IdGenerator {
private static final AtomicLong nextId = new AtomicLong(System
.currentTimeMillis());
public Long nextId() {
return nextId.getAndIncrement();
}
}
This one PASSES all tests I could imagine.
But what about jUnit ?
Once again TestNG has offered feature that I needed. I tried to find the similar solution for jUnit, but I couldn't. Well, there is a separate project parallel-junit, but come on, I have it out-of-the-TestNG-box, so why should I bother ?
| Attachment | Size |
|---|---|
| testng-parallel-0.1-SNAPSHOT-src.zip | 8.38 KB |