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.
Short answer
You have to unwrap the proxy and set the field on the target object:
The
unwrapFooService()
can be defined as follows:...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 toFooServiceImpl$EnhancerByCGLIB
. This subclass contains a reference to the originalFooServiceImpl
as well as... all the fieldsFooServiceImpl
has (which is understandable - this is a subclass).So there are actually two variables:
FooServiceImpl$EnhancerByCGLIB.fooDao
andFooServiceImpl.fooDao
. You are assigning a mock to the former but your service uses the latter... I wrote about this pitfalls some time ago.