Spring boot's actuator unavailable when set management port - java

I use Spring boot + Spring Security + Spring Actuator
My JUnit test class:
#RunWith(SpringRunner.class)
#SpringBootTest()
#AutoConfigureMockMvc
public class ActuatorTests {
#Autowired
private MockMvc mockMvc;
#Test
#WithMockUser(roles={"USER","SUPERUSER"})
public void getHealth() throws Exception {
mockMvc.perform(get("/health"))
.andExpect(status().isOk());
}
}
is OK, but when I set management.port: 8088, my test is KO with this message:
[ERROR] ActuatorTests.getHealth:37 Status expected:<200> but was:<404>
How to set management port in my JUnit test MockMvc or test configuration?

When management.port is different to server.port Spring will create a separate web application context and a dedicated servlet container where it will register all actuators. A default MockMvc routes requests against the main application web context and not the management one. That is what happening in your case - since no actuators are running in the main application web context you get a 404. To test endpoints running in a management context use the following setup:
#RunWith(SpringRunner.class)
#SpringBootTest
public class ManagementContextMvcTest {
#Autowired
private ManagementContextResolver resolver;
private MockMvc mockMvc;
#Before
public void setUp() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(
(WebApplicationContext) resolver.getApplicationContext()).build();
}
#Test
#WithMockUser(roles = { "USER", "SUPERUSER" })
public void getHealth() throws Exception {
mockMvc.perform(get("/health"))
.andExpect(status().isOk());
}
}

Related

Remove "Using default security password" on Spring Boot test

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());
}
}

Spring Boot controller test loading entire application context

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());
}
}

Spring Boot Aspectj test with MockMvc

I have a Spring boot code with Aspectj. This code has written with basic MVC architecture. Then I just try to test it with MockMVC. But when I try to test it, Aspectj doesn't interrupted. Is there a special configuration about Aspectj?
Controller:
#GetMapping("user/{userId}/todo-list")
public ResponseEntity<?> getWaitingItems(#RequestUser CurrentUser currentUser){
...handle it with service method.
}
Aspect:
#Pointcut("execution(* *(.., #RequestUser (*), ..))")
void annotatedMethod()
{
}
#Before("annotatedMethod() && #annotation(requestUser)")
public void adviseAnnotatedMethods(JoinPoint joinPoint, RequestUser requestUser)
{
...
}
Test:
#WebMvcTest(value = {Controller.class, Aspect.class})
#ActiveProfiles("test")
#ContextConfiguration(classes = {Controller.class, Aspect.class})
#RunWith(SpringJUnit4ClassRunner.class)
public class ControllerTest
{
#Autowired
private MockMvc mockMvc;
#Autowired
private WebApplicationContext webApplicationContext;
#Autowired
private Controller controller;
#MockBean
private Service service;
#Before
public void setUp()
{
mockMvc = MockMvcBuilders
.webAppContextSetup(webApplicationContext)
.build();
}
#Test
public void getWaitingItems() throws Exception
{
mockMvc.perform(get("/user/{userId}/todo-list", 1L))
.andExpect(status().isOk());
}
}
There is no need for a #SpringBootTest if you wanna do integration tests of specific controller (web layer) + your custom Aspect logic (AOP layer).
Try something like this
#WebMvcTest(controllers = {AnyController.class})
#Import({AopAutoConfiguration.class, ExceptionAspect.class})
public class ErrorControllerAdviceTest {
AnyController.class: controller under test
AopAutoConfiguration.class: Spring Boot auto-configuration of AOP
ExceptionAspect.class: class containing AOP logic
#Aspect
#Component
public class ExceptionAspect {}
Tested with Spring Boot 2.2.1.RELEASE and JUNIT5.
I am unsure, if my solution is technically the same like #Deadpool answers
Spring #WebMvcTest will only instantiate web layer and it will not load complete application context
However, in this test, Spring Boot instantiates only the web layer rather than the whole context.
In order to test Aspectj you need to load whole application context using #SpringBootTest annotation
The #SpringBootTest annotation tells Spring Boot to look for a main configuration class (one with #SpringBootApplication, for instance) and use that to start a Spring application context
So annotate the test using #SpringBootTest annotation
#SpringBootTest
#ActiveProfiles("test")
#RunWith(SpringRunner.class)
#AutoConfigureMockMvc
public class ControllerTest {
#Autowired
private MockMvc mockMvc;
#Autowired
private WebApplicationContext webApplicationContext;
#Autowired
private Controller controller;
#Before
public void setUp() {
mockMvc = MockMvcBuilders
.webAppContextSetup(webApplicationContext)
.build();
}
#Test
public void getWaitingItems() throws Exception {
mockMvc.perform(get("/user/{userId}/todo-list", 1L))
.andExpect(status().isOk());
}
}

Testing security in Spring Boot 1.4

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?

How do I wire up the correct Spring context to my Cucumber tests?

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

Categories

Resources