Question

This is a general question on how to unit-test a Java Class using mock objects.
I can summarize my problem with this example.
Let's say I've an Interface called MyInterface.java and a "TwoString" Object that doesn't override equals()

"TwoString.java"

   private String string1;
   private String string2;

   public TwoString(String string1, String string2) {
     this.string1 = string1;
     this.string2 = string2;
   }
   ...getters..setters..

"MyInterface.java"

void callMe(TwoString twoString);

Then I have "MyClass.java" Object. Its constructor accepts a concrete implementation of MyInterface.
MyClass methodToTest() contains the logic to create a TwoString oject in some way. Let's say that it will be created as

new TwoString("a","b")

So when methodToTest() is called it creates this TwoString object that will be passed to the Interface method callMe(TwoString twoString).

I basically want to mock the Interface. Create a MyClass object with this mock. Then verify that the mock method is called with a specific instance of TwoString.

I'm using EasyMock and this is some java code

"MyClassTest.java"

public void test() throws Exception {   
   MyInterface myInterfaceMock = createMock(MyInterface.class);
   MyClass myClass = new MyClass(myInterfaceMock);

   myInterfaceMock.callMe(new TwoString("a","b"));   <--- fails here
   expectLastCall();
   replay(myInterfaceMock);

   myClass.methodToTest();
   verify(myInterfaceMock);

Here comes the problem. The TwoString object that I'm expecting in the call

myInterfaceMock.callMe(new TwoString("a","b"));

is different from the one generated in MyClass.methodToTest() because TwoString.java doesn't override equals.

I can skip the problem on the TwoString instance using

myInterfaceMock.callMe((TwoString)anyObject());

but I want to be sure that the interface method is called with a specific instance of TwoString that contains "a" as string1 and "b" as string2.

In this case the TwoString object is very simple and it will be easy to override the equals method - but what if I need to check a more complex object.

Thanks

edit:

I'll try to make it more readable with this example

public class MyClassTest {
    private MyClass myClass;
    private TaskExecutor taskExecutorMock;

    @Before
    public void setUp() throws Exception {
        taskExecutorMock = createMock(TaskExecutor.class);
        myClass = new MyClass(taskExecutorMock);
    }

    @Test
    public void testRun() throws Exception {
        List<MyObj> myObjList = new ArrayList<MyObj>();
        myObjList.add(new MyObj("abc", referenceToSomethingElse));

        taskExecutorMock.execute(new SomeTask(referenceToSomethingElse,  ???new SomeObj("abc", referenceToSomethingElse, "whatever")));   <--- ??? = object created using data in myObjList
        expectLastCall();
        replay(taskExecutorMock);

        myClass.run(myObjList);

        verify(taskExecutorMock);
    }
}

???SomeObj = object created by myClass.run() using data contained in myObjList.
Let's say that SomeObj comes from a third party library and it doesn't override equals.

I want to be sure that the taskExecutorMock.execute() method is getting called with a specific instance of that SomeObj

How can I test that the myClass.run() is actually calling the taskExecutorMock method with a correct instance

Was it helpful?

Solution

It is possible to use a custom equals matching method using org.easymock.IArgumentMatcher.

It should look something like:

private static <T extends TwoString> T eqTwoString(final TwoString twoString) {
    reportMatcher(new IArgumentMatcher() {
        /** Required to get nice output */
        public void appendTo(StringBuffer buffer) {
            buffer.append("eqTwoString(" + twoString.getString1() + "," + twoString.getString2() + ")");
        }

        /** Implement equals basically */
        public boolean matches(Object object) {
            if (object instanceof TwoString) {
                TwoString other = (TwoString) object;
                // Consider adding null checks below
                return twoString.getString1().equals(object.getString1()) && twoString.getString2().equals(object.getString2());
            }
            else {
                return false;
            }
        }
    });
    return null;
}

And is used as follows:

myInterfaceMock.callMe(eqTwoString(new TwoString("a","b")));

Some details may not be correct, but in essence it's how I've done it before. There is another example and more thorough explanations available in the EasyMock documentation. Just search for IArgumentMatcher.

OTHER TIPS

First up - you probably mean "override equals" rather than implement, since all classes have some implementation of equals (the one they inherit from Object if they don't override anything else).

The answer in this case is simple - all value objects really really ought to implements equals and hashcode. Whether it's a simple one like TwoString, or the more complex object you allude to, it should be the object's responsibility to check whether it is equal to some other object.

The only other alternative would be to basically deconstruct the object in your test code - so instead of

assertEquals(expected, twoStr);

you'd do

assertEquals(expected.getStringOne(), twoStr.getStringOne());
assertEquals(expected.getStringTwo(), twoStr.getStringTwo());

Hopefully you can see that this is bad in at least three ways. Firstly, you're basically duplicating the logic that should be in the class' own equals() method; and anywhere that you want to compare these objects you'll have to write the same code.

Secondly, you can only see the object's public state, there could well be some private state that causes two apparently similar objects to be not equal (e.g. a Lift class could have a publically accessible "floor" attribute, but private "going up or down" state too).

Finally, it's a violation of the Law of Demeter for a third-party class to be basically messing about with the internals of TwoString trying to work out whether the things are equal.

The object itself should implement its own equals() method - pure and simple.

Take a look at Jakarta Commons Lang: EqualsBuilder.reflectionEquals()

While I agree with dtsazza that all value objects should have an equals() (and hashCode()) method, they're not always appropriate to testing: most value objects will base equality on a key, rather than on all fields.

At the same time, I'm leery of any test that wants to check all fields: it seems to me to be saying "make sure this method didn't change something that I wasn't planning for it to change." Which is a valid test, and on some level a very well-meaning test, but it's a little scary that you feel the need for it.

In this case the TwoString object is very simple and it will be easy to override the equals method - but what if I need to check a more complex object.

Once your objects start becoming so complex that you can't trivially check if they're equal from elsewhere, you should probably refactor and inject them as a dependency. This would change the design, but usually that's for the better.

You also seem to be relying on some knowledge of the internal behaviour of your classes. The above is an interaction test between two classes, which still sort of works, but the bigger your set of tested components gets, the less you can really still talk about "unit" tests. At a certain point you leave the realm of unit tests and you start doing integration tests, in which case you might be better off with a full blown test harness and isolating behaviour in certain places...

You can achieve this with argument captors in Mockito 1.8.

http://mockito.googlecode.com/svn/branches/1.8.0/javadoc/org/mockito/Mockito.html#15

I know you are using EasyMock but changing to Mockito is easy and it's much better!

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top