JUnit is a unit testing framework for the Java programming language. JUnit has been important in the development of test-driven development, and is one of a family of unit testing frameworks which is collectively known as xUnit that originated with SUnit.
JUnit was originally written by Erich Gamma and Kent Beck. The latter literally wrote the book on Test Driven Development. I thought it would be worthwhile to look at some of the tests in the library and draw attention to some interesting things about them because it may change people’s ideas of how to practice TDD.
TDD is often explained in overly simple terms and this is probably done to increase adoption. Unfortunately, if you jump right in without a mentor, you will most likely approach the process in the wrong way. I think a lot of people usually wander blind in the woods and when they finally find their way out, they’ve picked up some dogma and cargo cult practices along the way.
My hope is that pointing out some of these interesting things about these tests will make people change their mind about how they practice TDD in the future. Or, if you already do these things I’m about to mention, it may reassure you that you’re practicing TDD the right way.
I know that Kent Beck didn’t write all these tests himself, but I’m confident he or someone he trusts code reviews them before they get committed to the project. I also want to say to take all of these things with a grain of salt. These were tests written for JUnit, not the project you’re working on. Just because the JUnit tests are written a certain way doesn’t mean that’s the best way to write your tests. But, it’s a great reason to consider doing so.
You do not have to mock out every collaborator
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
@Test(expected = AssertionError.class) public void arraysNotEqual() { assertArrayEquals((new Object[]{new Object()}), (new Object[]{new Object()})); } //inside org.junit.Assert public static void assertArrayEquals(Object[] expecteds, Object[] actuals) { assertArrayEquals(null, expecteds, actuals); } public static void assertArrayEquals(String message, Object[] expecteds, Object[] actuals) throws ArrayComparisonFailure { internalArrayEquals(message, expecteds, actuals); } private static void internalArrayEquals(String message, Object expecteds, Object actuals) throws ArrayComparisonFailure { new ExactComparisonCriteria().arrayEquals(message, expecteds, actuals); } |
If you follow into assertArrayEquals , you’ll see that it eventually calls internalArrayEquals which uses the ExactComparisonCriteria directly instead of mocking it out. The takeaway is, you don’t have to mock every collaborator.
You don’t have to write a test class for every production class
The production code has a package named org.junit.internal and it’s rare to find test code directly testing the classes or methods in that package. Instead, this code is indirectly tested by testing code that uses the classes in the org.junit.internal package. This is probably done to make it easier to refactor code without tests interfering. You can read more about this technique here.
You can have multiple asserts in one test
1 2 3 4 5 6 7 8 9 10 11 |
@Test public void oneDimensionalPrimitiveArraysAreEqual() { assertArrayEquals(new boolean[]{true}, new boolean[]{true}); assertArrayEquals(new byte[]{1}, new byte[]{1}); assertArrayEquals(new char[]{1}, new char[]{1}); assertArrayEquals(new short[]{1}, new short[]{1}); assertArrayEquals(new int[]{1}, new int[]{1}); assertArrayEquals(new long[]{1}, new long[]{1}); assertArrayEquals(new double[]{1.0}, new double[]{1.0}, 1.0); assertArrayEquals(new float[]{1.0f}, new float[]{1.0f}, 1.0f); } |
Your test package does not have to be the same package as the System Under Test
For example, there is a test named JUnit4ClassRunnerTest that is in the org.junit.tests.deprecated package, but it is testing a class in the org.junit.internal.runners package.
Your test classes can get kind of long, but your test methods should be pretty short
For example, the org.junit.tests.assertion.AssertionTest is close to 700 lines of code. But that is distributed between 80+ tests! Here’s an average sized one:
1 2 3 4 5 6 7 8 9 10 |
@Test public void notSameWithMessage() { Object o = new Object(); try { assertNotSame("message", o, o); fail(); } catch (AssertionError exception) { assertEquals("message expected not same", exception.getMessage()); } } |
Because this code is so short, it’s very easy to figure out what it’s doing.
The names of local variables in your test are not that important
1 2 3 4 5 6 7 |
@Test public void intersectionText() { NamedFilter a = new NamedFilter("a"); NamedFilter b = new NamedFilter("b"); assertEquals("a and b", a.intersect(b).describe()); assertEquals("b and a", b.intersect(a).describe()); } |
I see lots of local variables in tests with very poor names like a and o . But, as you can see, the tests are so straight forward that it doesn’t hinder readability.
It’s ok to create nested classes inside your test classes
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
public class ClassLevelMethodsWithIgnoredTestsTest { private static final String FAILURE_MESSAGE = "This should not have happened!"; public static class BeforeClassWithIgnoredTest { @BeforeClass public static void beforeClass() { fail(FAILURE_MESSAGE); } @Ignore @Test public void test() throws Exception { fail("test() should not run"); } } ... |
Nested classes are defined all over the tests. It’s relatively rare to find top level classes in the test package that aren’t test classes, test suites, or test runners. To relate this back to me, I’ve built a lot of FooBuilder’s in the past to make it easier to create production classes, but concepts like that don’t seem to exist in the JUnit test code. I did find a few classes that look like this at the top level:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
package junit.tests.framework; import junit.framework.TestCase; /** * A test case testing the testing framework. */ public class Success extends TestCase { @Override public void runTest() { } public void testSuccess() { } } |
I’m not sure why they decided to create this class at the top level, but a lot of other classes are nested in tests. My first impression is it has to do with whether the class will be reused in other tests are not, but the nested classes have a lot of duplication in them so that does not seem to be the reason.
Like this section says, it’s ok to nest classes in your tests, but from JUnit’s perspective, this isn’t a rule. It’s just a slight preference.
Assertions should almost always be at the end of a test
It was very rare for me to find tests that were written like this:
1 2 3 4 5 6 |
logic1(); assert1(); logic2(); assert2(); logic3(); assert3(); |
Most of the time, they were written like this:
1 2 3 4 5 6 |
logic1(); logic2(); logic3(); assert1(); assert2(); assert3(); |
Every test has at least one assert
Every test I’ve seen has at least one assert somewhere. In the past I have written tests that assert nothing. Their purpose was to check that I can create production classes or run methods without an error occurring (e.g., testInitializingFooDoesNotError() ). Based on this analysis, I’m going to stop doing that in the future.
You do not have to use a mocking framework or mocks
None of the tests I found use a mocking framework. In fact, technically I couldn’t find tests that use mocks. But there are a lot of tests that use stubs. This is an article on the difference, if you’re interested.
Every time I see some code that requires a parameter, that parameter is a real class, not a mock:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
@RunWith(JUnit4.class) static public class SimpleTestWithFutureProofExplicitRunner { @Test public void success() { run = true; } } public void testAnnotatedMethodWithFutureProofExplicitRunner() throws Exception { JUnitCore runner = new JUnitCore(); runner.run(SimpleTestWithFutureProofExplicitRunner.class); assertTrue(run); } |
You should definitely take this part a grain of salt though. For all I know, the JUnit team would use a mocking framework if they could. What makes it extra tricky is a lot of the test doubles they create need specific annotations on them and mocking frameworks may make that difficult to do.
You can create a test class for something that is not a production class
A lot of people write one test per production class. Sometimes that’s true for JUnit tests, but not always. For example there is a test named org.junit.tests.running.methods.AnnotationTest , but there is no Annotation class. This is just testing that the JUnit annotations (of which there are many) work correctly.
You can create your own, higher level asserts in your test
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
public void testRunSuccessResultCanBeSerialised() throws Exception { JUnitCore runner = new JUnitCore(); Result result = runner.run(Success.class); assertResultSerializable(result); } private void assertResultSerializable(Result result) throws IOException, ClassNotFoundException { ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); new ObjectOutputStream(byteArrayOutputStream).writeObject(result); byte[] bytes = byteArrayOutputStream.toByteArray(); ObjectInputStream objectInputStream = new ObjectInputStream(new ByteArrayInputStream(bytes)); Result fromStream = (Result) objectInputStream.readObject(); assertSerializedCorrectly(result, fromStream); InputStream resource = getClass().getResourceAsStream(getName()); assertNotNull("Could not read resource " + getName(), resource); objectInputStream = new ObjectInputStream(resource); fromStream = (Result) objectInputStream.readObject(); assertSerializedCorrectly(new ResultWithFixedRunTime(result), fromStream); } |
Here, the test method is very short, but it calls another method that does some complex logic for the assertion named assertResultSerializable() . By the way, if you look at the implementation of that method, this is one of the rare examples I found where assertions are spread out between logic. Usually all the assertions are at the end of a method.
Conclusion
I hope you found this exercise interesting. One of the great things about open source is the ability to download and read a famous project and try to become a better developer by inspecting it.
I thing it is only applicable for java till 1.5 through a refference of my knowledge….. Let me know that, Is it applicable for java 1.7?
Yeah this is all applicable for 1.7. Actually, it’s language agnostic. The code just happens to be Java.