JUnit Practices 1 : Let the Tests Talk

Each variant of the assertion in JUnit has a distinct purpose, used in a right context the assertions yield useful information on test failures. It is possible to construct tests with an assertion that is intend for a different purpose and make it work as expected; when we do so we may possibly loosing the useful information that a failure trace can give us.

Let us explore a couple of test scenarios and see what it yields to

Testing for equality, lets take case of Strings, the equality can be tested in two ways

  1. assertEquals(expected_string, actual_string)
    @Test
    public void testStringWithAssertTrue() {
    assertTrue("abc".equals("vdc"));
    }
  2. asserTure(expected_string.equals(actual_string))
     @Test
    public void testStringWithAssertTrue() {
    assertTrue("abc".equals("vdc"));
    }

Both of these will serve the purpose of testing, but the question is will they give the same insight on a test failure?
The test failure trace for the approach 1 is
java.lang.AssertionError at org.junit.Assert.fail(Assert.java:86)
at org.junit.Assert.assertTrue(Assert.java:41)
at org.junit.Assert.assertTrue(Assert.java:52) 

failure trace for the approach 2 is
org.junit.ComparisonFailure: expected:<[ab]c> but was:<[vd]c>
at org.junit.Assert.assertEquals(Assert.java:115)
at org.junit.Assert.assertEquals(Assert.java:144) 

The second approach gives a better perspective of the failure; it clearly says whats expected and what is actual.

Use assertEquals(obj1, obj2) to test the equality, avoid assertTrue(obj1.equals(obj2))

Testing for identity equality, i.e. testing if two objects are same. Lets say we are testing a singleton class. We could possibly assert it in two ways

  1. assertTrue(singleton1 == singleto2);
    @Test
    public void faultySingletonMultiThread () throws InterruptedException, ExecutionException {
    ExecutorService exec = Executors.newFixedThreadPool(2);
    Callable job = () -> FaultySingleton.getInstance();
    Future fSingleTonFuture1 = exec.submit(job);
    Future fSingleTonFuture2 = exec.submit(job);
    assertTrue(fSingleTonFuture1.get() == fSingleTonFuture2.get());
    } 
  2. assertSame(singleton1, singleto2);
    @Test
    public void faultySingletonMultiThread () throws InterruptedException, ExecutionException {
    ExecutorService exec = Executors.newFixedThreadPool(2);
    Callable job = () -> FaultySingleton.getInstance();
    Future fSingleTonFuture1 = exec.submit(job);
    Future fSingleTonFuture2 = exec.submit(job);
    assertSame(fSingleTonFuture1.get(), fSingleTonFuture2.get());
    } 

The failure trace of approach 1 is
java.lang.AssertionError
at org.junit.Assert.fail(Assert.java:86)
at org.junit.Assert.assertTrue(Assert.java:41)
at org.junit.Assert.assertTrue(Assert.java:52)

and the failure trace of approach 2 is
java.lang.AssertionError: expected same:<org.practprogrammer.junitpractices.FaultySingleton@6477463f> was not:<org.practprogrammer.junitpractices.FaultySingleton@3d71d552>
at org.junit.Assert.fail(Assert.java:88)

The failure trace of approach 2 is more clear and one can clearly understand that the test expects the same object.

Use assertSame(obj1, obj2) to test the identity equality, avoid assertTrue(obj1 == obj2)

To conclude,

In almost all the scenarios, it is possible to use assertTrue to verify the results. By doing so, we are muting the test, we aren’t letting the failure trace talk. Use appropriate assertion variant and let the tests talk .

The source code for the examples used in this article can be found @ my github