I have a multi module project, I noticed that when I run my tests (for example the tests annotated with #WebMvcTest) I get this message
Using generated security password: 12e4c462-385v-12y6-917u-e8u5rte36ooi
This generated password is for development use only. Your security configuration must be updated before running your application in production.
How do i remove it?
I think the "problem" is just that having a multi module project, in some tests, the class implementing UserDetailsService is not found because it is part of a different module and therefore the package is not scanned.
Is it enough for me to just ignore the message?
Actually this didn't happen before, it has happened since I removed a bean, probably useless, inside the WebSecuriyConfig class which extends the WebSecurityConfigurerAdapter.
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
Since I don't use that bean anywhere in my application.
Security configuration is not loaded by default by #WebMvcTest. You need to manually #Import your web-security config and then setup test security-context.
For configuring OAuth2 test security context, you can use either
MockMvc request post-processor: jwt() or opaqueToken() from org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors)
a test annotation from this repo
Sample with #WithMockJwtAuth
#WebMvcTest()
#Import({ WebSecurityConfig.class })
class GreetingControllerTest {
#MockBean
JwtDecoder jwtDecoder;
#Autowired
MockMvc mockMvc;
#Test
#WithMockJwtAuth(authorities = {"NICE", "AUTHOR"}, claims = #OpenIdClaims(preferred_username = "Tonton Pirate"))
void whenGrantedNiceRoleThenOk() throws Exception {
mockMvc.perform(get("/greet")).andExpect(status().isOk())
.andExpect(content().string("Hi Tonton Pirate! You are granted with: [NICE, AUTHOR]."));
}
#Test
#WithMockJwtAuth(authorities = {"AUTHOR"}, claims = #OpenIdClaims(preferred_username = "Tonton Pirate"))
void whenNotGrantedNiceRoleThenForbidden() throws Exception {
mockMvc.perform(get("/greet")).andExpect(status().isForbidden());
}
#Test
void whenAnonymousThenUnauthorized() throws Exception {
mockMvc.perform(get("/greet")).andExpect(status().isUnauthorized());
}
}
Same sample with jwt post-processor
#WebMvcTest()
#Import({ WebSecurityConfig.class })
class GreetingControllerTest {
#MockBean
JwtDecoder jwtDecoder;
#Autowired
MockMvc mockMvc;
#Test
void whenGrantedNiceRoleThenOk() throws Exception {
mockMvc.perform(get("/greet").with(jwt().jwt(jwt -> {
jwt.claim("preferred_username", "Tonton Pirate");
}).authorities(List.of(new SimpleGrantedAuthority("NICE"), new SimpleGrantedAuthority("AUTHOR"))))).andExpect(status().isOk())
.andExpect(content().string("Hi Tonton Pirate! You are granted with: [NICE, AUTHOR]."));
}
#Test
void whenNotGrantedNiceRoleThenForbidden() throws Exception {
mockMvc.perform(get("/greet").with(jwt().jwt(jwt -> {
jwt.claim("preferred_username", "Tonton Pirate");
}).authorities(List.of(new SimpleGrantedAuthority("AUTHOR"))))).andExpect(status().isForbidden());
}
#Test
void whenAnonymousThenUnauthorized() throws Exception {
mockMvc.perform(get("/greet")).andExpect(status().isUnauthorized());
}
}
Related
Spring Boot here. I currently have the following REST controller:
#RestController
public class FizzbuzzController {
private final FizzbuzzService FizzbuzzService;
public FizzbuzzController(FizzbuzzService FizzbuzzService) {
this.FizzbuzzService = FizzbuzzService;
}
#PostMapping("/Fizzbuzzs/{fizzbuzzId}")
public ResponseEntity<FizzbuzzDTO> addFizzbuzz(#RequestParam("files") List<MultipartFile> files,
#PathVariable String fizzbuzzId) throws IOException {
FizzbuzzDTO fizzbuzzDTO = fizzbuzzService.store(files, fizzbuzzId);
return ResponseEntity.status(HttpStatus.OK).body(fizzbuzzDTO);
}
}
I would like to write an integration test for it that:
Mocks or stubs an HTTP request to the URL; and
Allows me to inject the FizzbuzzController (under test) with a mocked FizzbuzzService or the real thing; and
Allows me to inspect the HTTP response coming back from the method (check status code, check response entity, etc.)
My best attempt thus far:
#WebMvcTest(FizzbuzzController.class)
#EnableConfigurationProperties
#AutoConfigureMockMvc
public class FizzbuzzControllerTest {
#Autowired
private MockMvc mockMvc;
#MockBean
private FizzbuzzService FizzbuzzService;
#Test
public void should_store_fizzbuzz_files() throws Exception {
// I can't even get the test to run
assertTrue(1 == 1);
}
}
When I run this, the test fails to run and it is clear (looking at the logs) that Spring is loading the entire application context of my app, whereas I just want it to isolate the context to this test class, the main FizzbuzzController class, and anything in the dependency tree underneath it.
Can anyone spot where I'm going awry?
You need another context for testing. I'm suggesting you to have a separate Test config:
#TestConfiguration
#Slf4j
#EnableJpaRepositories("tth.patientportal.repository")
public class TestConfig { // bean configs goes here for testing if you need to change
// context}
and in a controller test build the context like below:
#RunWith(SpringRunner.class)
#AutoConfigureTestEntityManager
#SpringBootTest
#TestPropertySource("classpath:application-unittest.properties")
#ContextConfiguration(classes = {TestConfig.class})
public class RestControllerTest {
#Autowired
private WebApplicationContext webApplicationContext;
private MockMvc mockMvc;
#Before
public void setup()
{
mockMvc = MockMvcBuilders.
webAppContextSetup(webApplicationContext)
.build();
}
#Test
public void shouldReturnRegisteredUser() throws Exception {
this.mockMvc.
perform(MockMvcRequestBuilders
.post("url")
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(MockMvcResultMatchers.jsonPath("$.username").exists());
}
}
I have a Spring application,
and I've created this test:
#RunWith(SpringRunner.class)
#SpringJUnitWebConfig(locations = {
"classpath:testDatabaseContext.xml",
"classpath:testServicesContext.xml",
"classpath:backoffice-servlet.xml"
})
public class UserControllerTests {
#Autowired
private MockMvc mockMvc;
#Before
void setup(WebApplicationContext wac) {
this.mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
}
..
}
but when I start the test I got this error:
rg.junit.runners.model.InvalidTestClassError: Invalid test class 'com.pastis.UserControllerTests':
1. Method setup() should be public
2. Method setup should have no parameters
Here an example:
RunWith(SpringJUnit4ClassRunner.class)
#WebAppConfiguration
#ContextConfiguration(classes = MyWebConfig.class)
public class CustomerControllerTest {
#Autowired
private WebApplicationContext wac;
private MockMvc mockMvc;
#Before
public void setup () {
DefaultMockMvcBuilder builder = MockMvcBuilders.webAppContextSetup(this.wac);
this.mockMvc = builder.build();
}
#Test
public void testUserController () throws Exception {
ResultMatcher ok = MockMvcResultMatchers.status()
.isOk();
MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.get("/customers");
this.mockMvc.perform(builder)
.andExpect(ok);
}
}
So I explain the output of the exception:
Add the modifier public to your method setup, otherwise JUnit can't invoke it
Remove the parameter from the method, #Before, #After and such don't allow parameters.
How to setup MockMvc correctly is another question. Recent Spring and additional annotations regarding web scope initialization and -behavior leave the scope of this answer. (This also requires more clarification, for example which JDK, which Spring or Spring Boot… XML configuration, dbunit and JUnit 4 suggest a legacy context.)
Can't make tests working with such a configuration.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration
#WebAppConfiguration
#TestPropertySource(value="classpath:app-test.properties")
public class LoginTest {
#Autowired
private WebApplicationContext context;
MockMvc mockMvc;
#InjectMocks
private AuthController authController;
#Test
public void testOk() throws Exception {
MockitoAnnotations.initMocks(this);
mockMvc = MockMvcBuilders
.webAppContextSetup(context)
.apply(springSecurity())
.build();
mockMvc.perform(get("/api/auth")
.with(httpBasic("u", "p")))
.andExpect(status().isOk());
}
}
Getting error:
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.
If you see your stacktrace , you can note that this is thrown at
SecurityMockMvcConfigurer.java (a spring lib class) and if your see the code at that line inside in this class , exception is raised from below code.
public static MockMvcConfigurer More ...springSecurity(Filter springSecurityFilterChain) {
Assert.notNull(springSecurityFilterChain,
"springSecurityFilterChain cannot be null");
return new SecurityMockMvcConfigurer(springSecurityFilterChain);
}
which means one bean springSecurityFilterChain is needed and that is how SecurityMockMvcConfigurer is build.
Thus you need to provide this autowired bean,
If you already have this bean , Then make it available
#Autowired
FilterChainProxy springSecurityFilterChain;
#Test
public void testOk() throws Exception {
MockitoAnnotations.initMocks(this);
mockMvc = MockMvcBuilders
.webAppContextSetup(context)
.apply(SecurityMockMvcConfigurers.springSecurity(springSecurityFilterChain))
//.setCustomArgumentResolvers(new AuthenticationPrincipalArgumentResolver()) // if you dont have that bean then try with this line and delete above line.
.build();
mockMvc.perform(get("/api/auth")
.with(httpBasic("u", "p")))
.andExpect(status().isOk());
}
I'm trying to test #WebMvcTest with custom security settings defined in SecurityConfig class:
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/admin*").access("hasRole('ADMIN')").antMatchers("/**").permitAll().and().formLogin();
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("user").password("password").roles("ADMIN");
}
}
Test class is:
#RunWith(SpringRunner.class)
#WebMvcTest(value = ExampleController.class)
public class ExampleControllerMockMVCTest {
#Autowired
private MockMvc mockMvc;
#Test
public void indexTest() throws Exception {
mockMvc.perform(get("/"))
.andExpect(status().isOk())
.andExpect(view().name("index"));
}
#Test
public void adminTestWithoutAuthentication() throws Exception {
mockMvc.perform(get("/admin"))
.andExpect(status().is3xxRedirection()); //login form redirect
}
#Test
#WithMockUser(username="example", password="password", roles={"ANONYMOUS"})
public void adminTestWithBadAuthentication() throws Exception {
mockMvc.perform(get("/admin"))
.andExpect(status().isForbidden());
}
#Test
#WithMockUser(username="user", password="password", roles={"ADMIN"})
public void adminTestWithAuthentication() throws Exception {
mockMvc.perform(get("/admin"))
.andExpect(status().isOk())
.andExpect(view().name("admin"))
.andExpect(model().attributeExists("name"))
.andExpect(model().attribute("name", is("user")));
}
}
Tests fail because they are using the default security settings of Spring Boot.
I can fix this using #SpringBootTest + #AutoConfigureMockMvc, but it would be interesting to test without running all auto-configuration.
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = WebEnvironment.MOCK)
#AutoConfigureMockMvc
public class ExampleControllerSpringBootTest {
#Autowired
private MockMvc mockMvc;
// tests
}
Is there any way that #WebMvcTest can use settings defined in SecurityConfig class?
WebMvcTest is only going to load your controller and nothing else (that's why we call that slicing). We can't figure out which part of your configuration you want and which one you don't. If the security config isn't on your main #SpringBootApplication you'll have to import it explicitly. Otherwise, Spring Boot is going to enable default security settings.
If you're using something like OAuth, that's a good thing though because you really don't want to start using that for a mock test. What happens if you add #Import(SecurityConfig.class) to your test?
I have a working integration test for my Spring Web MVC app that looks like this:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = ShibaApplication.class)
#WebAppConfiguration
public class EchoControllerTests {
private MockMvc mockMvc;
#Autowired
private WebApplicationContext webApplicationContext;
#Before
private void setup() throws Exception {
this.mockMvc = webAppContextSetup(webApplicationContext).build();
}
#Test
public void echo() throws Exception {
mockMvc.perform(get("/echo/blargh"))
.andExpect(status().isOk())
.andExpect(content().string("blargh"));
}
}
Leaving that (successful) test in place, I tried to create an identical Cucumber test. The Cucumber runner is:
#RunWith(Cucumber.class)
#CucumberOptions(features="src/test/resources",
glue={"co.masslab.shiba", "cucumber.api.spring"})
public class CucumberTests {
}
The class that defines the Cucumber steps looks like:
#WebAppConfiguration
#Import(ShibaApplication.class)
#ContextConfiguration(classes=CucumberTests.class)
public class WebStepDefs {
#Autowired
private WebApplicationContext webApplicationContext;
private MockMvc mockMvc;
private ResultActions resultActions;
#When("^the client calls the echo endpoint$")
public void the_client_calls() throws Exception {
Assert.notNull(webApplicationContext);
this.mockMvc = webAppContextSetup(webApplicationContext).build();
this.resultActions = mockMvc.perform(get("/echo/blargh"));
}
#Then("^the client receives a status code of 200$")
public void the_client_receives_a_status_code() throws Exception {
resultActions.andExpect(status().isOk());
}
}
However, the cucumber test fails, as the result is not a 200 but a 404.
I suspect this is because the WebApplicationContext getting autowired into the WebStepDefs class isn’t the same as the one that gets autowired into the EchoControllerTests. I’ve been going over the Spring JavaConfig Reference Guide v1.0.0.M4, but I haven’t yet figured out where I’m going wrong.
I kept trying different combinations of annotations, and finally figured this one out. The annotations for WebStepsDef that worked for me were:
#ContextConfiguration(classes=ShibaApplication.class, loader=SpringApplicationContextLoader.class)
#IntegrationTest
#WebAppConfiguration