28 September 2009

Scala and Testing

In our company, we have a rich and mature Java stack that we draw from for nearly every project. However, I politely told my manager that, for this project, we would be using Scala. I explained that we would still be able to use everything on the Stack, and we would get it done faster and cheaper that way.

So, happily, he gave me a little bit of line.

When it came to writing Unit Tests, we wanted to use EasyMock, an awesome Java library for mocking, stubbing, etc.

Everything worked great, but I ran into one little problem:

If I have the following code:


EasyMock.expect(myClass.myMethod(myParam)).andReturn(myReturn);
EasyMock.replay(myClass);
... do unit testing ...
EasyMock.verify(myClass);
EasyMock.reset(myClass);


Then, I get on the replay call:


java.lang.IllegalArgumentException: not a proxy instance
at java.lang.reflect.Proxy.getInvocationHandler(Proxy.java:637)
at org.easymock.EasyMock.getControl(EasyMock.java:1600)
at org.easymock.EasyMock.replay(EasyMock.java:1502)...


This is because I am not using the classextension.EasyMock class. However, if I use that, then Scala complains that "expect" isn't a member of classextension.EasyMock.

So, here is the solution that I came up with:

I use import shortnames to include both EasyMock classes:


import org.easymock.{EasyMock=>EM};
import org.easymock.classextension.{EasyMock=>CEM};


And then in the code, I do the following:


EM expect(myClass myMethod myParam) andReturn myReturn
CEM replay myClass
...unit test stuff...
CEM verify myClass
CEM reset myClass


And it works great!

Has anyone else found a good solution for this?

The next problem that I have run into is method matching when one of the values passed in is an array projection.

Observe the following code:


myClass.myMethod(param1, param2, str split "_" drop 1)


The last parameter returns type Array.Projection. If I have the exact same code in my expect, then this particular parameter is still listed as being unequal.

The only way that I have found so far around this is:


EM expect(myClass.myMethod(EM anyObject[type1], EM anyObject[type2], EM anyObject[type3])) andReturn myReturn


Not a big fan of this strategy, but it works. Any ideas?