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.
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 + ">");
}
}
}
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();
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With