Acess Target Object Behind a Proxy

Today I was facing a problem when I wanted to inject my mocked repository to my service in my test class using spring-boot-starter-test with spring boot. This is my test class :

@SpringApplicationConfiguration(classes = { TestApplication.class })
public class AppSecurityTest {

    private static final String LOGIN_CORRECT = "user1";

    private static final String LOGIN_NOT_CORRECT = "XXXXXXXX";

    private MockMvc mockMvc;

    private WebApplicationContext context;

    private Filter springSecurityFilterChain;

    private UserRepository userRepository;

    private UserDetailsService userDetailsServiceImpl;

     * setUp.
     * @throws Exception 
    public void setup() throws Exception {
    // Process mock annotations

    this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context).addFilters(this.springSecurityFilterChain)

    // mock userRepository result
    User user = new User();
    .thenThrow(new UsernameNotFoundException("No user found with username: " + LOGIN_NOT_CORRECT));
    // I get exception in this line
    ReflectionTestUtils.setField(userDetailsServiceImpl, "userRepository", userRepository);

Problem :

I get this exception when I run my test :

java.lang.IllegalArgumentException: Could not find field [userRepository] of type [null] on target []

The problem comes from the instance Autowired of UserDetailsServic, it is a proxy and not my real instance of UserDetailsServiceImpl.

Explanation :

When annotating spring managed beans with stuff like @Transactional, spring will behind the scenes produce code, that ensures that transaction logic is applied before and after your code. Depending on the configuration, this is often done using a JDK proxy, which is a dynamically generated class implementing the bean interfaces. This dynamically generated code will apply its logic and then dispatch the call on to what is called the "target object", that is, the object that has been proxied.

In my case as you see in the code above, I mock the userRepository and I want to inject it to the UserDetailsService, So the proxy contained in userDetailsServiceImpl don't have an attribute named userRepository.

Solution :

I created a method that return the native object from the proxy passed in params :

    protected <T> T getTargetObject(Object proxy) throws Exception {
        if (AopUtils.isJdkDynamicProxy(proxy)) {
            return (T) ((Advised) proxy).getTargetSource().getTarget();
        } else {
            return (T) proxy;

So I changed the line :
ReflectionTestUtils.setField(userDetailsServiceImpl, "userRepository", userRepository);

with :
ReflectionTestUtils.setField(this.<UserDetailsServiceImpl>getTargetObject(userDetailsServiceImpl), "userRepository", userRepository);

That's it :)

Recommended books :

comments powered by Disqus