10

I have an @Aspect that weaves the execution of all my controller action methods. It works fine when I run the system, but not in unit testing. I'm using Mockito and junit in the following way:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("file:**/spring-context.xml")
@WebAppConfiguration
public class UserControllerTest {        
    private MockMvc mockMvc;

    @Mock
    private RoleService roleService;

    @InjectMocks
    private UserController userController;

    @Before
    public void setUp() {
       MockitoAnnotations.initMocks(this);                    
       ...    
       mockMvc = MockMvcBuilders.standaloneSetup(userController).build();
    }    
    ...
}

with some @Test using mockMvc.perform().

And my Aspect are:

@Pointcut("within(@org.springframework.stereotype.Controller *)")
public void controller() { }

@Pointcut("execution(* mypackage.controller.*Controller.*(..))")
public void methodPointcut() { }

@Around("controller() && methodPointcut()")
...
Kalle Richter
  • 8,008
  • 26
  • 77
  • 177
Fabrício Lima
  • 101
  • 1
  • 1
  • 4
  • I have the same problem. I've noticed that aspects do fire if you use the alternate ```webAppContextSetup``` rather than ```standaloneSetup``` but in that case the mocks aren't injected into the controller. I've yet to figure out how to get both working – Jason Polites Oct 30 '13 at 20:05
  • See also https://stackoverflow.com/q/8121551/466738 – Adam Michalik Aug 07 '19 at 10:04

2 Answers2

12

First it is necessary to use webAppContextSetup as Jason suggested:

@Autowired
private WebApplicationContext webApplicationContext;

@Before
public void setUp() throws Exception {
   ...
   mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
}

At this point the aspect should be triggered but Mockito will not inject mocks. This is because Spring AOP uses a proxy object and the mocks are being injected to this proxy object instead of the proxied object. To fix this it is necessary to unwrap the object and use ReflectionUtils instead of @InjectMocks annotation:

private MockMvc mockMvc;

@Mock
private RoleService roleService;

private UserController userController;

@Autowired
private WebApplicationContext webApplicationContext;

@Before
public void setUp() {
   MockitoAnnotations.initMocks(this);                    
   UserController unwrappedController = (UserController) unwrapProxy(userController);
   ReflectionTestUtils.setField(unwrappedController, "roleService", roleService);   
   mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
}

...

public static final Object unwrapProxy(Object bean) throws Exception {
/*
 * If the given object is a proxy, set the return value as the object
 * being proxied, otherwise return the given object.
 */
    if (AopUtils.isAopProxy(bean) && bean instanceof Advised) {
        Advised advised = (Advised) bean;
        bean = advised.getTargetSource().getTarget();
    }
    return bean;
}

At this point any call to when(...).thenReturn(...) should work properly.

It is explained here: http://kim.saabye-pedersen.org/2012/12/mockito-and-spring-proxies.html

Kalle Richter
  • 8,008
  • 26
  • 77
  • 177
user3527787
  • 121
  • 1
  • 3
0

You are probably using Spring AOP, in which case the bean has to be a Spring bean for AOP to work, by not autowiring in the controller it is bypassing the Spring AOP mechanism totally.

I think the fix should be to simply inject in the controller

 @Autowired
 @InjectMocks
 private UserController userController;
Biju Kunjummen
  • 49,138
  • 14
  • 112
  • 125