Blake Caldwell: /dev/blog
Not a Java Developer

Asserting Exceptions With JUnit Rules: IsEqual Matcher

In my first post on asserting exceptions with JUint, I showed how to test that a specific type of exception is thrown with expected substrings in the error message. In my second post, I showed how we can write a custom [Matcher] to inspect the contents of the exception. In this post, I’ll show you how to take advantage of the stock IsEqual matcher to accomplish the same task, but with less work.

IsEqual Matcher

This Matcher is straightforward - it evaluates whether two objects are equal by calling equals(Object) on one of the objects that isn’t null, passing in the other. So, to use it with our custom exception, we’ll need to make sure that our equals(Object) method correctly evaluates two of its instances.

The default behavior of equals(Object obj) is to check if two objects are the same instance:

The equals method for class Object implements the most discriminating possible equivalence relation on objects; that is, for any non-null reference values x and y, this method returns true if and only if x and y refer to the same object (x == y has the value true).

This won’t help us here, because we’ll be comparing exceptions thrown in our code with one we instantiated in our unit test. We’re going to have to implement equals(Object) ourselves.

Implementing equals(Object)

Here’s my simple custom exception:

/**
 * Error Code Exception - stores the error code that was generated
 * when this exception was thrown.
 */
public class ErrorCodeException extends RuntimeException
{
    private static final long serialVersionUID = 1L;

    /**
     * The error code that triggered the exception.
     */
    private int errorCode;

    /**
     * Constructor.
     * 
     * @param errorCode
     *            the error code that triggered the exception
     */
    public ErrorCodeException(int errorCode)
    {
        this.errorCode = errorCode;
    }

    /**
     * Get the error code that triggered the exception.
     */
    public int getErrorCode()
    {
        return errorCode;
    }
}

You have to be careful when implementing either equals(Object) or hashCode(). The rule is that if you implement either of these methods, then you need to implement both. Two objects that are equal must have the same hash code. If they don’t, and you try to add two distinct, but equal instances of a class to a Map, they’d both be accepted. This is because internally, the Map stores the items in ‘buckets’ based on their hash codes, and then only has to check equality within a bucket. If two objects have different hash codes, then they won’t be compared to each other.

Your IDE should have the ability to generate what we need here. If you’re using Eclipse (I recommend the STS version, right-click in the source file, select “Source”, and the select “Generate hashCode() and equals()”.

Generate hashCode() and equals() menu

After selecting that option, choose which private members will be used in the two methods. I recommend selecting “‘Use blocks in ‘if’ statements” in order to help wrong code look wrong, should someone modify these methods down the road.

Generate hashCode() and equals()

Here’s our final ErrorCodeException class with the newly generated code:

package org.blakecaldwell.tests.junitrules;

/**
 * error code exception - stores the error code that was generated
 * when this exception was thrown.
 */
public class errorcodeexception extends runtimeexception
{
    private static final long serialversionuid = 1l;

    /**
     * the error code that triggered the exception.
     */
    private int errorcode;

    /**
     * constructor.
     * 
     * @param errorcode
     *            the error code that triggered the exception
     */
    public errorcodeexception(int errorcode)
    {
        this.errorcode = errorcode;
    }

    /**
     * get the error code that triggered the exception.
     */
    public int geterrorcode()
    {
        return errorcode;
    }

    /**
     * (non-javadoc)
     * 
     * @see java.lang.object#hashcode()
     */
    @override
    public int hashcode()
    {
        final int prime = 31;
        int result = 1;
        result = prime * result + errorcode;
        return result;
    }

    /**
     * (non-javadoc)
     * 
     * @see java.lang.object#equals(java.lang.object)
     */
    @override
    public boolean equals(object obj)
    {
        if (this == obj)
        {
            return true;
        }
        if (obj == null)
        {
            return false;
        }
        if (getclass() != obj.getclass())
        {
            return false;
        }
        errorcodeexception other = (errorcodeexception) obj;
        if (errorcode != other.errorcode)
        {
            return false;
        }
        return true;
    }
}

Verifying _equals(Object) and hashCode()

Even though we generated this code, we still need to test it. Here’s the test fixture for ErrorCodeException:

package org.blakecaldwell.tests.junitrules;

import org.junit.Assert;
import org.junit.Test;

/**
 * Test fixture for ErrorCodeException.
 */
public class ErrorCodeExceptionTest
{
    /**
     * Test getErrorCode().
     */
    @Test
    public void testGetErrorCode()
    {
        ErrorCodeException exception = new ErrorCodeException(500);

        Assert.assertEquals(500, exception.getErrorCode());
    }

    /**
     * Test equals(Object) evaluates true for two equal instances.
     */
    @Test
    public void testEqualsWhenEqual()
    {
        ErrorCodeException exception1 = new ErrorCodeException(500);
        ErrorCodeException exception2 = new ErrorCodeException(500);

        Assert.assertTrue(exception1.equals(exception2));

        // another way to test
        Assert.assertEquals(exception1, exception2);
    }

    /**
     * Test equals(Object) evaluates as false for two unequal
     * instances.
     */
    @Test
    public void testEqualsWhenNotEqual()
    {
        ErrorCodeException exception1 = new ErrorCodeException(500);
        ErrorCodeException exception2 = new ErrorCodeException(501);

        Assert.assertFalse(exception1.equals(exception2));

        // another way to test
        Assert.assertNotEquals(exception1, exception2);
    }

    /**
     * Test equals(Object) evaluates as false when we pass in null.
     */
    @Test
    public void testEqualsWhenNull()
    {
        ErrorCodeException exception1 = new ErrorCodeException(500);

        Assert.assertFalse(exception1.equals(null));

        // another way to test
        Assert.assertNotEquals(exception1, null);
    }

    /**
     * Test that two equal objects have the same hash code. Note that
     * two different objects don't need to have different hash codes.
     */
    @Test
    public void testHashCodeForTwoEqualObjects()
    {
        ErrorCodeException exception1 = new ErrorCodeException(500);
        ErrorCodeException exception2 = new ErrorCodeException(500);

        // sanity check - make sure they're equal
        Assert.assertEquals(exception1, exception2);

        // make sure they have the same hash code
        Assert.assertEquals(exception1.hashCode(), exception2.hashCode());
    }
}

Using the IsEqual Matcher In Our Unit Tests

Now that we’ve implemented equals(Object) and hashCode() for our custom exception, we can use the IsEqual Matcher to setup an expectation for a specific exception.

package org.blakecaldwell.tests.junitrules;

import org.hamcrest.core.IsEqual;
import org.junit.Assert;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;

/**
 * Test fixture to demonstrate JUnit's ExpectedException @Rule with an
 * IsEqual matcher.
 */
public class ExpectedExceptionRuleIsEqualMatcherTest
{
    /**
     * ExpectedException must be public.
     */
    @Rule
    public ExpectedException exception = ExpectedException.none();

    /**
     * Assert that we're throwing the exact exception that we're
     * expecting by using an IsEqual Matcher.
     */
    @Test
    public void testErrorCode500UsingIsEqualMatcher()
    {
        ErrorCodeException expectedException = new ErrorCodeException(500);

        exception.expect(new IsEqual<ErrorCodeException>(expectedException));

        // Run your code that you expect will throw an
        // ErrorCodeException with error code 500. If this exception
        // is equal to our expectedException above, this test will
        // pass.
        throw new ErrorCodeException(500);
    }

    /**
     * Here's the old way of asserting a specific exception. Yuck!
     */
    @Test
    public void testErrorCode500WithoutJUnitRules()
    {
        ErrorCodeException exception = null;
        try
        {
            throw new ErrorCodeException(500);
        }
        catch (Exception ex)
        {
            exception = (ErrorCodeException) ex;
        }
        Assert.assertEquals(500, exception.getErrorCode());
    }
}

In the first test, I create an IsEqual Matcher with the exception that I want to compare the thrown exception to. No custom Matcher was required, and my custom exception is now more useful because of it.

In my second test, I include the ‘old way’ of checking exceptions to demonstrate how much easier and more readable exception tests are when using JUnit’s Rules feature.




Asserting Exceptions With JUnit Rules: Custom Matchers

In my previous post, I demonstrated how to use JUnit’s Rules feature to assert expected assertions in your unit tests. In this post, I’ll show you how to write custom Matchers that will help give you more power when inspecting your exceptions.

Maven Dependencies

This demo uses the following Maven dependencies:

<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.11</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.hamcrest</groupId>
    <artifactId>hamcrest-library</artifactId>
    <version>1.3</version>
</dependency>

Custom Exception

We’ll start with a custom exception which does little more than remember the error code at the time the exception was thrown.

/**
 * Error Code Exception - stores the error code that was generated
 * when this exception was thrown.
 */
public class ErrorCodeException extends RuntimeException
{
    private static final long serialVersionUID = 1L;

    /**
     * The error code that triggered the exception.
     */
    private int errorCode;

    /**
     * Constructor.
     * 
     * @param errorCode
     *            the error code that triggered the exception
     */
    public ErrorCodeException(int errorCode)
    {
        this.errorCode = errorCode;
    }

    /**
     * Get the error code that triggered the exception.
     */
    public int getErrorCode()
    {
        return errorCode;
    }
}

Our Exception Matcher

When we pass our Matcher to JUnit’s ExpectedException instance, we’re given a chance to match the exception itself, not the message. In this case, we’re going to write a Matcher that makes sure that the exception’s error code was as expected. We can only match on an instance of our _ ErrorCodeException_, so we’ll save some effort and extend TypeSafeMatcher.

From the documentation:

TypeSafeMatcher : Convenient base class for Matchers that require a non-null value of a specific type. This simply implements the null check, checks the type and then casts.

import org.hamcrest.Description;
import org.hamcrest.TypeSafeMatcher;

/**
 * ErrorCodeMatcher - matches when an ErrorCodeException has the same
 * error code as expected.
 */
public class ErrorCodeExceptionMatcher extends TypeSafeMatcher<ErrorCodeException>
{
    /**
     * The error code we're expecting.
     */
    private int expectedErrorCode;

    /**
     * Constructor.
     * 
     * @param expectedErrorCode
     *            the error code that we're expecting
     */
    public ErrorCodeExceptionMatcher(int expectedErrorCode)
    {
        this.expectedErrorCode = expectedErrorCode;
    }

    /**
     * Describe the error condition.
     */
    @Override
    public void describeTo(Description description)
    {
        description.appendText("Error code doesn't match");
    }

    /**
     * Test if the input exception matches the expected exception.
     */
    @Override
    protected boolean matchesSafely(ErrorCodeException exceptionToTest)
    {
        return exceptionToTest.getErrorCode() == this.expectedErrorCode;
    }

}

Example Tests With ExpectedException and Our Custom Matcher

With the components in place, let’s start testing. Of course, if you only have one or two error test cases, then a custom Matcher might take more work than it saves, but you end up with code that any developer should be able to read, which might reduce maintenance costs.

package org.blakecaldwell.tests.junitrules;

import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;

/**
 * Test fixture to demonstrate JUnit's ExpectedException @Rule with a
 * custom matcher.
 */
public class ExpectedExceptionRuleCustomMatcherTest
{
    /**
     * ExpectedException must be public.
     */
    @Rule
    public ExpectedException exception = ExpectedException.none();

    /**
     * Test exception error code 500 using our custom Matcher.
     */
    @Test
    public void testErrorCode500UsingMatcher()
    {
        exception.expect(new ErrorCodeMatcher(500));

        // Run your code that you expect will throw an
        // ErrorCodeException with
        // error code 500
        throw new ErrorCodeException(500);
    }

    /**
     * Test exception error code 404 using our custom Matcher.
     */
    @Test
    public void testErrorCodeUsingMatcher()
    {
        exception.expect(new ErrorCodeMatcher(404));

        // Run your code that you expect will throw an
        // ErrorCodeException with
        // error code 404
        throw new ErrorCodeException(404);
    }
}

Asserting Exceptions The Old Way

To demonstrate the benefits of using custom Matchers with JUnit’s ExcpectedException, here are the alternatives that you’re probably familiar with, with inline comments explaining why they’re not ideal.

package org.blakecaldwell.tests.junitrules;

import org.junit.Assert;
import org.junit.Test;

/**
 * Tests that demonstrate how to assert exceptions without
 * ExpectedException @Rule and custom Matchers.
 */
public class ExceptionAssertingOldWayTest
{
    /**
     * Test that an ErrorCodeException was thrown with the "expected"
     * parameter. The limitation here is that you can only test the
     * type of Exception, not its parameters or the message.
     */
    @Test(expected = ErrorCodeException.class)
    public void testErrorCodeExceptionThrownOldWay1()
    {
        // Run your code that you expect will throw an
        // ErrorCodeException with
        // error code 500
        throw new ErrorCodeException(500);
    }

    /**
     * Test exception error code 404 by capturing the Exception and
     * inspecting it inside the catch.
     * 
     * Remarks: We have to make sure to assert failure after our code
     * was supposed to throw the exception, or we won't know if it
     * never does. This nuance makes this method error prone for
     * future modifications.
     */
    @Test
    public void testErrorCodeExceptionThrownOldWay2()
    {
        try
        {
            // The "A".equals("A") is here just for this demo to
      // prevent an "Unreachable code" warning in Eclipse, since
      // we're throwing the exception directly instead of
      // calling code that throws it.
            if ("A".equals("A"))
            {
                // Run your code that you expect will throw an
                // ErrorCodeException with error code 404
                throw new ErrorCodeException(404);
            }

            // if we've gotten to this point, then no Exception was
            // thrown
            Assert.fail("Test case failed: no exception thrown");
        }
        catch (Exception ex)
        {
            Assert.assertEquals(
                    "Expected 404 error code exception",
                    404,
                    ((ErrorCodeException) ex).getErrorCode());
        }
    }

   /**
    * Test exception error code 404 by capturing the Exception and
    * inspecting it outside the try/catch.
    * 
    * Remarks: This is safer than asserting inside the catch (as
    * above), since if the exception is never thrown, our assertion
    * won't be valid, but still is not a very elegant solution.
    */
    @Test
    public void testErrorCodeExceptionThrownOldWay3()
    {
        Exception caughtException = null;
        try
        {
            // Run your code that you expect will throw an
            // ErrorCodeException
            // with error code 404
            throw new ErrorCodeException(404);
        }
        catch (Exception ex)
        {
            caughtException = ex;
        }

        // if the exception was never thrown, or was thrown and is an
        // incorrect
        // type or wrong code, this will fail
        Assert.assertEquals(
                "Expected 404 error code exception",
                404,
                ((ErrorCodeException) caughtException).getErrorCode());
    }
}



Asserting Exception Messages With JUnit Rules

If you’re not familiar with JUnit’s @Rule feature for asserting exceptions in your tests, then read on - you’re about to start using it.

Assert Exception Type

It’s very simple to assert that a given type of exception is thrown in a JUnit test case with the following:

public void testExceptionTypeIsThrown()
{
    throw new RuntimeException("Error!");
}

Assert Exception Message (The Old Way)

But, what if you want to be more specific, and check the message itself? I’ve always done the following:

/**
 * Test the exception message the old way.
 */
@Test
public void testExceptionMessageTheOldWay1()
{
    String message = null;
    try
    {
        throw new RuntimeException("Error!");
    }
    catch(Exception e)
    {
        message = e.getMessage();
    }
    Assert.assertEquals("Error!", message);
}

Heres’s another variant you’re probably familiar with:

/**
 * Test the exception message the old way.
 */
@Test
public void testExceptionMessageTheOldWay2()
{
    try
    {
        // this string test was necessary to avoid an "unreachable code" warning in Eclipse
        if ("A".equals("A"))
        {
            throw new RuntimeException("Error!");
        }
        Assert.fail("Exception was expected.");
    } catch (Exception e)
    {
        Assert.assertEquals("Error!", e.getMessage());
    }
}

Assert Exception Message With JUnit Rules

The above methods always felt like hacks. I recently came across JUnit’s @Rule feature, which saves tons of code and is much easier to read. You first define your public ExpectedException instance, and give it a @Rule annotation. Then, in each test case that wants to use it, you set what type of exception you’re expecting, and optionally a substring to look for in the exception message:

/**
 * Our expected exception rule. This must be public.
 */
@Rule
public ExpectedException exception = ExpectedException.none();

/**
 * Test the exception message with a single expectMessage substring match.
 */
@Test
public void testExceptionIsThrownFullText()
{
    exception.expect(RuntimeException.class);
    exception.expectMessage("Error!");

    throw new RuntimeException("Error!");
}

Since expectMessage is looking for substrings, you can use several of them to test more complicated exception messages:

/**
 * Our expected exception rule. This must be public.
 */
@Rule
public ExpectedException exception = ExpectedException.none();

/**
 * Test the exception message with multiple expectMessage substrings.
 */
@Test
public void testExceptionIsThrownMultipleSubstrings()
{
    exception.expect(RuntimeException.class);
    exception.expectMessage("Error: There was an issue processing ");
    exception.expectMessage(" of the notes.");

    throw new RuntimeException("Error: There was an issue processing 5 of the notes.");
}

More Advanced: Custom Matchers

In my next post, I’ll i describe how to implement a custom Matcher for more complicated Exception assertions.




Conditionally Run JUnit Integration Tests with Spring

Ideally, your unit test suites require no external dependencies - those should be mocked out. However, sometimes you want extra assurance that your code works with live endpoints via integration tests.

For example, you might want to make sure that your code can successfully navigate your corporate proxy and firewall to make external web requests. For this, we’ll write tests that only run when a command line parameter is defined.

This demonstration assumes that you’re using Spring’s JUnit Runner and Maven for running your tests. The @IfProfileValue annotation will tell the test runner to only include this test suite if our configured ProfileValueSource returns “true” for “live-external-tests-enabled”.

package org.blakecaldwell;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.annotation.IfProfileValue;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@IfProfileValue(name="live-external-tests-enabled", value="true")
public class IntegrationTest
{
    @Test
    public void testExternalConnection()
    {
        Assert.fail("testExternalConnection failed!");
    }
}

Let’s verify that this test suite is ignored by default with the maven command:

mvn test

You’ll see that you now have a skipped test:

Results:

Tests run: 1337, Failures: 0, Errors: 0, Skipped: 1

Now, try it again, but this time with our test included:

mvn test -Dlive-external-tests-enabled=true

You’ll now see that the test was run:

Results :

Tests in error: 
  testExternalConnection(org.blakecaldwell.IntegrationTest): Failed to load ApplicationContext

Tests run: 1337, Failures: 0, Errors: 1, Skipped: 0

The @IfProfileValue annotation can also be used above an individual test.




Object/Relational Mapping: Know Your Frameworks

I’ve been working with Hibernate for several years now, yet I learn something new about it all the time. The more time I spend with the framework, the more concerned I am about how it will be used by developers new to it.

Mirko Novakovic Alois Reitbauer nails it in a post about O/R Mapping Anti-Patterns:

The simplicity of the entrance into the world of O/R mapping however gives a wrong impression of the complexity of these frameworks. Working with more complex applications you soon realize that you should know the details of framework implementation to be able to use them in the best possible way. In this article, we describe some common anti-patterns which may easily lead to performance problems.

This is an echo of Joel Spolsky’s warnings of the Law of Leaky Abstraction:

The law of leaky abstractions means that whenever somebody comes up with a wizzy new code-generation tool that is supposed to make us all ever-so-efficient, you hear a lot of people saying “learn how to do it manually first, then use the wizzy tool to save time.” Code generation tools which pretend to abstract out something, like all abstractions, leak, and the only way to deal with the leaks competently is to learn about how the abstractions work and what they are abstracting. So the abstractions save us time working, but they don’t save us time learning.

Don’t stop learning about a framework once you figure out how to use it - that’s only the beginning.