views:

166

answers:

4

In a test class, I’d like to provide my own overload of assertEquals with some special logic not relying on Object.equals. Unfortunately, that doesn’t work because as soon as I declare my assertEquals method locally, Java doesn’t find the static import from org.junit.Assert.* any more.

Is there a way around this? I.e. is there a way to provide an additional overload to a statically imported method? (The rather obvious solution being to name the method differently but this solution doesn’t have the same aesthetic appeal.)

My test class file looks something like this:

package org.foo.bar;

import static org.junit.Assert.*;

import org.junit.Test;

public class BarTest {
    private static void assertEquals(Bar expected, Bar other) {
        // Some custom logic to test equality.
    }

    @Test
    public void testGetFoo() throws Exception {
        Bar a = new Bar();
        assertEquals(42, a.getFoo()); // Error *
    }

    @Test
    public void testCopyConstructor() throws Exception {
        Bar a = new Bar();
        // Fill a.
        Bar b = new Bar(a);
        assertEquals(a, b);
    }
}

Error * is “The method assertEquals(Bar, Bar) in the type BarTest is not applicable for the arguments (int, int).”

+1  A: 

The only way is to fully qualify one or the other.

import static org.junit.Assert.*;

import org.junit.Test;

public class BarTest {

    private static void assertEquals(Bar expected, Bar other) {
        // Some custom logic to test equality.
    }

    @Test
    public void testGetFoo() throws Exception {
        Bar a = new Bar();
        org.junit.Assert.assertEquals(42, a.getFoo());
    }
}
SingleShot
+3  A: 

There are two sections in this answer - one about the compilation error, and the other about the usage of assertEquals()

The problem is that there are two assertEquals() methods in two different namespaces - one present in the org.junit.Assert namespace, the other in the org.foo.bar.BarTest namespace (the current namespace).

The error is reported by the compiler due to the shadowing rules declared in the Java Language Specification. The static import of Assert.assertEquals() is shadowed by the assertEquals() declared in the BarTest class.

The fix (always in the case of shadowed declarations) is to use FQNs (Fully Qualified Names). If you intend to use assertEquals(...) of the JUnit Assert class, use

org.junit.Assert.assertEquals(...)

and when you need to use your declaration simply use

assertEquals(...)

in BarTest only, where it is shadowed. In all other classes that require only either of Assert.assertEquals() or BarTest.asserEquals(), you can import Assert or BarTest (I don't think you would need to import BarTest elsewhere, but stated it nevertheless).

When there is no shadowing, you can afford to simply import the class or static method and use it without FQNs.

Additional stuff to think about

Assert.assertEquals() internally utilizes the equals() method of the arguments' classes. Declaring an assertEquals() in your test case, violates the DRY principle, since the equals() method of type should be implemented and used consistently - put two different implementations in the source code and in the unit tests is bound to cause confusion.

The best approach would be to implement equals() on Bar, and then use Assert.assertEquals() in your test cases. If you already have, the you don't need a BarTest.assertEquals(). The pseudocode for assertEquals() is somewhat like the following

  1. If both arguments are null, return true.
  2. If expected is not null, then invoke equals() on expected passing the actual as the argument. Return true if the object are equal.
  3. If the objects are not equal, throw an AssertionError with a formatted message.
Vineet Reynolds
I don’t really think that this violates DRY (rather POLS, since we’re using acronyms): `equals` intentionally doesn’t exist on my type because it makes absolutely no sense for these types to be equality comparable – except for this unit test – so they don’t implement it.
Konrad Rudolph
A tricky situation, which makes it debatable on whether to have an equals() implementation or to shadow assertEquals(). I would go with equals().
Vineet Reynolds
+2  A: 

One possible solution for your specific example of calling assertEquals(Bar, Bar) in a unit test would be to extend the class with one which provides the static method, as follows:

class BarAssert extends Assert {
  public static void assertEquals(Bar expected, Bar other) {
        // Some custom logic to test equality.
    }
}

You could then include import static BarAssert.assertEquals; and use your custom logic.

Apologies that this doesn't directly answer the question, and is more aimed at your example. As per my comment attached to the question, I would recommend against this approach.

Grundlefleck
+1. Anyone reading this and using this approach should also throw AssertionError on failure. Test runners would then be able to flag a failed test in red.
Vineet Reynolds
That would be java.lang.AssertionError http://java.sun.com/j2se/1.5.0/docs/api/java/lang/AssertionError.html
Vineet Reynolds
@Vineet: why not simply use `Assert.fail`?
Konrad Rudolph
@Konrad, my bad. I forgot that fail() is available when you subclass. Internally it creates and throws the AssertionError.
Vineet Reynolds
A: 
this.assertEquals(a,b);

or

BarTest.assertEquals(a,b);

I'd go with the first one, since despite it being a static method, you'll have to have an instance to use it (it's private) and this won't be subject to the whimsy of future renamings.

Carl