Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring Security @WithMockUser does not work with cucumber Tests

I am using cucumber tests to test my spring boot app with spring security enabled .Things work fine except when I run my test suite with cucumber tests some tests using spring security eg.

    @WithMockUser(username = "BROWSER", roles =
   {"BROWSER","ADMIN"})

fail .These tests do work if I do run them in seclusion as simple junit tests but fail when run with cucumber test steps. The issue looks like the spring security test mock behaviour isnt getting applied when I run the same with cucumber tests.

My cucumber test run class is as below

@RunWith(Cucumber.class)
@CucumberOptions(features = "src/test/resources", monochrome = true, format =
{"pretty", "html:src/main/resources/static/cucumber"})
public class CucumberTests
{

}

Also I noticed the same works when run via Maven with <reuseForks>false</reuseForks> .Also maven triggered test case run also fails if this option is not checked .

UPDATE

AbstractIntegrationTest class all tests extend

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = Services.class,loader = SpringApplicationContextLoader.class)
//@IntegrationTest
@WebIntegrationTest(randomPort = true)
public abstract class AbstractIntegrationTest {

Another use case which does not work is using theses annotation is cucumber feature conditions like below

@When("^I apply a GET on (.*)$")
    @WithMockUser(username = "BROWSER", roles = { "BROWSER", "ADMIN" })
    public void i_search_with_rsql(String query) throws Throwable {
    result = mvc.perform(get(uri, query));
    }

any help or workaround on this.

like image 666
Gaurav Rawat Avatar asked Apr 12 '16 21:04

Gaurav Rawat


2 Answers

WithMockUser does not work with Cucumber. Use cucumber hooks instead.

WithMockUser relies on TestExecutionListener#beforeTestMethod from Spring's test context support, but they are not invoked when running with Cucumber runner. This is because Cucumber runs scenarios composed of steps rather than the standard JUnit test methods.

Option 1 - Security context hooks. You can setup security context with hooks, for example:

@ActiveProfiles("test")
@SpringBootTest(classes = MyServer.class)
@AutoConfigureMockMvc
@AutoConfigureRestDocs
@AutoConfigureCache
public class MyServerContextHooks {

  @Before
  public void onBeforeScenario(final Scenario scenario) {
    // This method does nothing - context setup is done with annotations
  }
}

Example annotation on scenarios:

@WithAdminUser
Scenario: Run action as admin
    ...

Example hook to use annotation on scenarios:

public class TestUserHooks {

  @Before("@WithAdminUser")
  public void setupAdminUser() {
    SecurityContextHolder.getContext().setAuthentication(
            new UsernamePasswordAuthenticationToken(
                "admin",
                "N/A",
                createAuthorityList("admin")));         
  }
}

Option 2 - Authentication steps. Another way is to use special steps for providing user into mockMvc:

Scenario: Run action as admin
    Given I am logged in as admin
    ...

Stepdef example:

public class SecurityTestSteps {

  @Autowired
  private MockMvcGlue mockMvc;

  @Autowired
  private OAuth2Mocks oauth2Mocks;

  @Autowired
  private TestUsers users;

  /**
   * Provides a one of predefined role-based authentications for the current request.
   */
  @Given("^I am logged in as (admin|editor|user)$")
  public void given_UserIsAuthenticatedWithRole(final String role) {
    switch (role) {
      case "admin":
        mockMvc.request().with(authentication(oauth2Mocks.auth(users.admin())));
        break;
      case "editor":
        mockMvc.request().with(authentication(oauth2Mocks.auth(users.edtior())));
        break;
      default:
        throw new CucumberException("Unsupported role <" + role + ">");
    }
  }
}
like image 88
aux Avatar answered Sep 22 '22 03:09

aux


Base upon your comments you need to ensure to apply Spring Security. You can find an example of this in the Setting Up MockMvc and Spring Security section of the reference documentation:

import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.*;

// ...

@Before
public void setup() {
    mvc = MockMvcBuilders
            .webAppContextSetup(context)
            .apply(springSecurity()) // ADD THIS!
            .build();
}
like image 40
Rob Winch Avatar answered Sep 23 '22 03:09

Rob Winch