Question

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?

Was it helpful?

Solution 2

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.

OTHER TIPS

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

public static <T> 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.

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.

I know this question is old, but I just stumbled upon it searching for a similar problem.

The problem is that Spring doesn't know the type of the mock object. The method you use looks like this:

public static <T> T createMock(final Class<T> toMock) {
    return createControl().createMock(toMock);
}

Spring isn't smart enough to derive T from the constructor argument (at least the last time I've checked), so it thinks the returned object is of type java.lang.Object. As a consequence, the created proxy doesn't implement my.project.Service and therefore can't be injected.

The answer therefore is to tell Spring the required type.

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