Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring MockMVC, Spring security and Mockito

I'd like to test a Spring Boot Rest controller, which is secured using Spring security, and use mocks inside it. I have tried with Mockito, but I think any mocking tool should do the trick.

To enable Spring security in my tests, I first did as follow:

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = Main.class)
@TestPropertySource(value="classpath:application-test.properties")
@WebAppConfiguration
@ContextConfiguration
public class MyTest{

    protected MockMvc mockMvc;

    @Autowired
    private WebApplicationContext wac;

    @Before
    public void setUp(){
        mockMvc = MockMvcBuilders
                .webAppContextSetup(wac)
                .apply(SecurityMockMvcConfigurers.springSecurity())
                .build();
    }

    @Test
    public void doTheTest(){
        mockMvc.perform(post("/user/register")
            .with(SecurityMockMvcRequestPostProcessors.csrf())
            .content(someContent()));
    }
}

Until there, it works well.

After this step, I wished to add mocks to test my secured controller in isolation.

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = Main.class)
@TestPropertySource(value="classpath:application-test.properties")
@WebAppConfiguration
@ContextConfiguration
public class MyTest{

    protected MockMvc mockMvc;

    @Mock
    private Myservice serviceInjectedInController;

    @InjectMocks
    private MyController myController;

    @Autowired
    private WebApplicationContext wac;

    @Before
    public void setUp(){
        mockMvc = MockMvcBuilders
                .webAppContextSetup(wac)
                .apply(SecurityMockMvcConfigurers.springSecurity())
                .build();
    }

    @Test
    public void doTheTest(){
        mockMvc.perform(post("/user/register")
            .with(SecurityMockMvcRequestPostProcessors.csrf())
            .content(someContent()));
    }
}

Unfortunately, the mocked service is not injected in the controller, as there is nothing relating the MockMVC and the Mocks, so the mocks are not injected in the controller.

So I tried changing the configuration of the MockMVC, as follows:

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = Main.class)
@TestPropertySource(value="classpath:application-test.properties")
@WebAppConfiguration
@ContextConfiguration
public class MyTest{

    protected MockMvc mockMvc;

    @Mock
    private Myservice serviceInjectedInController;

    @InjectMocks
    private MyController myController;


    @Before
    public void setUp(){
        mockMvc = MockMvcBuilders
                .standAloneSetup(myController)
                .apply(SecurityMockMvcConfigurers.springSecurity())
                .build();
    }

    @Test
    public void doTheTest(){
        mockMvc.perform(post("/user/register")
            .with(SecurityMockMvcRequestPostProcessors.csrf())
            .content(someContent()));
    }
}

But in this case, I have another issue. Spring security is complaining about the configuration:

java.lang.IllegalStateException: springSecurityFilterChain cannot be null. Ensure a Bean with the name springSecurityFilterChain implementing Filter is present or inject the Filter to be used.

I have no other idea to make security and mocking. Any idea? Or should I do another way?

Thanks.

like image 225
Rémi Doolaeghe Avatar asked Nov 04 '15 14:11

Rémi Doolaeghe


People also ask

What is the use of MockMvc in spring boot?

MockMvc provides support for Spring MVC testing. It encapsulates all web application beans and makes them available for testing. We'll initialize the mockMvc object in the @BeforeEach annotated method, so that we don't have to initialize it inside every test.

What is MockMvc?

MockMvc is defined as a main entry point for server-side Spring MVC testing. Tests with MockMvc lie somewhere between between unit and integration tests.

How does MockMvc perform work?

MockMVC class is part of Spring MVC test framework which helps in testing the controllers explicitly starting a Servlet container. In this MockMVC tutorial, we will use it along with Spring boot's WebMvcTest class to execute Junit testcases which tests REST controller methods written for Spring boot 2 hateoas example.


1 Answers

By default the integration looks for a bean with the name of "springSecurityFilterChain". In the example that was provided, a standalone setup is being used which means MockMvc will not be aware of the WebApplicationContext provided within the test and thus not be able to look up the "springSecurityFilterChain" bean.

The easiest way to resolve this is to use something like this:

    MockMvc mockMvc = MockMvcBuilders
            // replace standaloneSetup with line below
            .webAppContextSetup(wac)
            .alwaysDo(print())
            .apply(SecurityMockMvcConfigurers.springSecurity())
            .build();

If you really want to use a standaloneSetup (doesn't really make sense since you already have a WebApplicationContext), you can explicitly provide the springSecurityFilterChain using:

@Autowired
FilterChainProxy springSecurityFilterChain;

@Before
public void startMocks(){
    controller = wac.getBean(RecipesController.class);

    MockMvc mockMvc = MockMvcBuilders
            .standaloneSetup(controller)
            .alwaysDo(print())
            .apply(SecurityMockMvcConfigurers.springSecurity(springSecurityFilterChain))
            .build();

    MockitoAnnotations.initMocks(this);
}
like image 101
Rob Winch Avatar answered Sep 18 '22 12:09

Rob Winch