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 :

@ActiveProfiles("test")
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = { TestApplication.class })
@EnableConfigurationProperties
@WebAppConfiguration
public class AppSecurityTest {

    private static final String LOGIN_CORRECT = "user1";

    private static final String LOGIN_NOT_CORRECT = "XXXXXXXX";

    private MockMvc mockMvc;

    @Autowired
    private WebApplicationContext context;

    @Autowired
    private Filter springSecurityFilterChain;

    @Mock
    private UserRepository userRepository;

    @Autowired
    private UserDetailsService userDetailsServiceImpl;

    /**
     * setUp.
     * @throws Exception 
     */
    @Before
    public void setup() throws Exception {
    // Process mock annotations
    MockitoAnnotations.initMocks(this);

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

    // mock userRepository result
    User user = new User();
    user.setUsername(LOGIN_CORRECT);
    user.setPassword(PASS_BCRYPT_CORRECT);
    when(userRepository.findByUsername(eq(LOGIN_CORRECT))).thenReturn(user);
    when(userRepository.findByUsername(eq(LOGIN_NOT_CORRECT)))
    .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 [com.entreprise.business.service.impl.UserDetailsServiceImpl@3c777ad1]

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