views:

1705

answers:

3

I am trying to get Spring to inject EasyMock mocks in my unit tests.

In my applicationContext.xml, I have this:

<bean id="mockService"  class="org.easymock.EasyMock" factory-method="createMock"  name="MockService">
    <constructor-arg index="0" value="my.project.Service"/>
</bean>

In my unit test I have this:

@Autowired
@Qualifier("mockService")
private Service service;

public void testGetFoo() {
    Foo foo = new Foo();

    expect(service.findFoo()).andReturn(foo);
    replay(service); // <-- This is line 45, which causes the exception

    // Assertions go here...
}

When I try to run my test, I get this stack trace:

java.lang.ClassCastException: org.springframework.aop.framework.JdkDynamicAopProxy
at org.easymock.EasyMock.getControl(EasyMock.java:1330)
at org.easymock.EasyMock.replay(EasyMock.java:1279)
at TestFooBar.testGetFoo(TestVodServiceLocator.java:45)

I am quite new to both Spring and EasyMock, but it seems to me that the error is caused by EasyMock trying to call a method on what it assumes to be an instance of EasyMock, but is in reality a dynamic proxy created by Spring. As I understand it, dynamic proxies only implement the methods defined in the interface, in this case the interface for Service.

What I don't understand is that from what I read (also here), what I'm trying to achieve at least seems to be possible.

So my question is: What I'm I not doing or what am I doing wrong?

+2  A: 

Solved!

I had overlooked this in my applicationContext.xml:

<bean id="txProxyAutoCreator" class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
    <property name="beanNames">
        <list>

            <value>*Service</value>
            <!--   ^^^^^^^^    
               This is the problem!
            -->
        </list>
    </property>
    <property name="interceptorNames">
        <list>
            <value>txAdvisor</value>
        </list>
    </property>
</bean>

... which causes Spring to automatically create proxy objects for all beans with names that end in "Service".

My solution was to explicitly list the beans instead of using a wild card. This seems a little brittle to me, however, so if anyone knows how to specify all *Service beans except FooService, I would be grateful.

KaptajnKold
+1  A: 

Something's odd here. You're clearly quickly mastering both Spring and EasyMock. The autoproxies, the factory methods, all good signs that you're diving deep into Spring's capabilities.

Still, it's kind of odd that you're injecting a mock bean into a class at all. You may have a great reason, but to me it's a code smell. I'd advise you to consider only wiring in real services into your test classes and then initializing the mock objects as needed. Why spend 3 lines in java and another 3 lines in XML (plus 1 line to reset them) to create a mock object with no dependencies? Just say Service service = (Service)createMock(Service.class). I would advise creating them in the methods that you need them for, set expectations, inject, use them, and then discard them. In your model, you'll have to remember to reset the mock object every time you use it, which is less clear than just creating a new one.

Of course, this is a style issue and not a correctness issue, so ignore as desired.

CaptainAwesomePants
I simplified my example somewhat before posting it, to make simpler to understand.The thing is that the class under test happens to be responsible for getting a particular bean from the application context at run time based on it's name. It seemed obvious to define that bean as a mock in the xml-file.Still, I understand your point, and you're right. I think I got some things mixed together and went a little too far.
KaptajnKold
This becomes usefull if you want to integration test advice execution or wiring. This way you can e.g. check whether transactions are applied correctly.
Oliver Gierke
+4  A: 

You can also create a helper method to unwrap the EasyMock proxy from the Spring proxy to define expected behaviour then:

public static <T> unwrap(T proxiedInstance) {
  if (proxiedInstance instanceof Advised) {
    return unwrap((T) ((Advised) proxiedInstance).getTargetSource().getTarget());
  }

  return proxiedInstance;
}

Note the recusive call as in worst case you have multiple proxies wrapped around the actual target.

Oliver Gierke
I really like that method!
CaptainAwesomePants