18

I'm having an issue when trying to mock a property of a service from within a Junit test:

@ContextConfiguration("classpath:application-config.xml")
@RunWith(SpringJUnit4ClassRunner.class)
public class FooServiceTests {

    @Autowired
    private FooServiceImpl fooService;

    @Test
    public void testFoo() {
        String str = fooService.foo();
        assertEquals("Var", str);
    }

    @Before
    public void mockFooDao() throws Exception {
        FooDao mockFooDao = Mockito.mock(FooDao.class);
        Mockito.when(mockFooDao.foo()).thenReturn("Var");
        ReflectionTestUtils.setField(fooService, "fooDao", mockFooDao);
    }
}

Mocking fooDao has no effect since the the result is not the expected. Here is the code of both the service and the dao:

@Service("fooService")
public class FooServiceImpl implements FooService {

    @Autowired
    protected FooDao fooDao;

    @Override
    public String foo() {
        return fooDao.foo();
    }
}

@Repository
public class FooDaoImpl implements FooDao {

    @Override
    public String foo() {
        return "foo";
    }
}

As we can see the actual service is meant to return "foo", but the test mocks the dao so the service returns "var". I know it's a CGLIB proxy related thing but I can't figure out how to make it work without using a setter for the fooDao property. Any help would be appreciated.

Regards and thanks in advance.

franDayz
  • 883
  • 10
  • 22

1 Answers1

41

Short answer

You have to unwrap the proxy and set the field on the target object:

ReflectionTestUtils.setField(unwrapFooService(), "fooDao", mockFooDao);

The unwrapFooService() can be defined as follows:

private FooServiceImpl unwrapFooService() {
  if(AopUtils.isAopProxy(fooService) && fooService instanceof Advised) {
      Object target = ((Advised) fooService).getTargetSource().getTarget();
      return (FooServiceImpl)target;
  }
  return null;
}

...long one

The problem is quite complex, but solvable. As you have guessed this is a side-effect of CGLIB proxies being used. In principle, Spring creates a subclass of your FooServiceImpl named similar to FooServiceImpl$EnhancerByCGLIB. This subclass contains a reference to the original FooServiceImpl as well as... all the fields FooServiceImpl has (which is understandable - this is a subclass).

So there are actually two variables: FooServiceImpl$EnhancerByCGLIB.fooDao and FooServiceImpl.fooDao. You are assigning a mock to the former but your service uses the latter... I wrote about this pitfalls some time ago.

Community
  • 1
  • 1
Tomasz Nurkiewicz
  • 334,321
  • 69
  • 703
  • 674
  • @frandiaz83: glad I could help! Consider [accepting](http://meta.stackexchange.com/questions/5421) and/or upvoting correct answer to point future readers to the proper solution. – Tomasz Nurkiewicz Jan 30 '12 at 07:32
  • Yes, of course. I've accepted the answer but I can't vote up since I don't have enough reputation... Thanks again. – franDayz Jan 30 '12 at 07:35
  • There were some problems with `unwrapFooService()`: unresolved name `a` and missing return in case the condition is not true. I took the liberty to edit it so that it compiles & works (for me). Also, +1 – Jonik Oct 09 '12 at 13:01
  • 5
    No need to write `unwrapFooService` yourself anymore with Spring 4.2. Check out http://stackoverflow.com/a/30461953/2504224 – geoand May 26 '15 at 14:53