19

recently I changed my spring boot properties to define a management port. In doing so, my unit tests started to fail :(

I wrote a unit test that tested the /metrics endpoint as follows:

@RunWith (SpringRunner.class)
@DirtiesContext
@SpringBootTest
public class MetricsTest {

    @Autowired
    private WebApplicationContext context;

    private MockMvc mvc;

    /**
     * Called before each test.
     */
    @Before
    public void setUp() {
        this.context.getBean(MetricsEndpoint.class).setEnabled(true);
        this.mvc = MockMvcBuilders.webAppContextSetup(this.context).build();
    }

    /**
     * Test for home page.
     *
     * @throws Exception On failure.
     */
    @Test
    public void home()
            throws Exception {
        this.mvc.perform(MockMvcRequestBuilders.get("/metrics"))
                .andExpect(MockMvcResultMatchers.status().isOk());
    }
}        

Previously this was passing. After adding:

management.port=9001

The tests started failing with:

home Failed: java.lang.AssertionError: Status expected: <200> but was: <404>

I tried changing the @SpringBootTest annotation with:

@SpringBootTest (properties = {"management.port=<server.port>"})

Where is the number used for the server.port. This didn't seem to make any difference.

So then changed the management.port value in the property file to be the same as the server.port. Same result.

The only way to get the test to work is remove the management.port from the property file.

Any suggestions/thoughts ?

Thanks

Jonathan
  • 20,053
  • 6
  • 63
  • 70
WHGIBBO
  • 199
  • 1
  • 2
  • 7
  • So adding some tracing logging for the success and failing cases. In the success case I see the following: `TRACE [Test worker] --- org.springframework.test.web.servlet.TestDispatcherServlet: Testing handler map [org.springframework.boot.actuate.endpoint.mvc.EndpointHandlerMapping@2281521a] in DispatcherServlet with name ` This is missing in the failing case.. So I can only assume that I'm using the wrong value for `MockMvcBuilders.webAppContextSetup(this.context)` – WHGIBBO Jul 21 '16 at 15:08

11 Answers11

4

For Spring boot test we need to specify the port it needs to connect to.

By default, it connects to server.port which in case of actuators is different.

This can be done by

@SpringBootTest(properties = "server.port=8090")

in application.properties we specify the management port as below

...
management.server.port=8090
...
du-it
  • 2,561
  • 8
  • 42
  • 80
MirzaM
  • 51
  • 2
4

For Spring Boot 2.x the integration tests configuration could be simplified.

For example simple custom heartbeat endpoint

@Component
@Endpoint(id = "heartbeat")
public class HeartbeatEndpoint {

    @ReadOperation
    public String heartbeat() {
        return "";
    }
}

Where integration test for this endpoint

@SpringBootTest(
        classes = HeartbeatEndpointTest.Config.class,
        properties = {
                "management.endpoint.heartbeat.enabled=true",
                "management.endpoints.web.exposure.include=heartbeat"
        })
@AutoConfigureMockMvc
@EnableAutoConfiguration
class HeartbeatEndpointTest {

    private static final String ENDPOINT_PATH = "/actuator/heartbeat";

    @Autowired
    private MockMvc mockMvc;

    @Test
    void testHeartbeat() throws Exception {
        mockMvc
                .perform(get(ENDPOINT_PATH))
                .andExpect(status().isOk())
                .andExpect(content().string(""));
    }

    @Configuration
    @Import(ProcessorTestConfig.class)
    static class Config {

        @Bean
        public HeartbeatEndpoint heartbeatEndpoint() {
            return new HeartbeatEndpoint();
        }

    }

}    
Dmytro Boichenko
  • 5,217
  • 3
  • 28
  • 31
3

Did you try adding the following annotation to your test class?

@TestPropertySource(properties = {"management.port=0"})

Check the following link for reference.

Viswanath
  • 1,413
  • 13
  • 25
3

Isn't there an error in the property name? Shouldn't be @TestPropertySource(properties = {"management.server.port=..."}) instead of @TestPropertySource(properties = {"management.port=.."})

Diamond
  • 217
  • 1
  • 3
  • 7
3

The guide stated that this can be achieved with @AutoConfigureMetrics. And I moved with this.

Regardless of your classpath, meter registries, except the in-memory backed, are not auto-configured when using @SpringBootTest. If you need to export metrics to a different backend as part of an integration test, annotate it with @AutoConfigureMetrics.

https://docs.spring.io/spring-boot/docs/current/reference/html/features.html#features.testing.spring-boot-applications.metrics

gosshys
  • 31
  • 1
1

Had the same issue, you just have to make the management.port null by adding this in your application-test.properties (set it to empty value)

management.port=

Make sure you use the test profile in your JUnit by annotating the class with

@ActiveProfiles("test")
flob
  • 116
  • 5
0

Try using

@SpringBootTest(properties = {"management.port="})

Properties defined in the @SpringBootTest annotation have a higher precedence than those in application properties. "management.port=" will "unset" the management.port property.
This way you don't have to worry about configuring the port in your tests.

binoternary
  • 1,865
  • 1
  • 13
  • 25
0

I was facing the same issue and tried several things but this is how I was able to solve mine without making any change in the application.yaml

Sample actuator endpoint

@Component
@RestControllerEndpoint(id = "endpoint")
public class SampleEndpoint
{
    @GetMapping
    public String sampleEndpoint(){
      return ""
    }
}

Unit test case

@RunWith(SpringRunner.class)
@SpringBootTest(
    classes = {SampleEndpointTest.Config.class},
    properties = {"management.server.port="}
)
@AutoConfigureMockMvc
public class SampleEndpointTest
{
    @Autowired
    private MockMvc mockMvc;

    @SpringBootApplication(scanBasePackageClasses = {SampleEndpoint.class})
    public static class Config
    {
    }

    @Test
    public void testSampleEndpoint() throws Exception
    {

        mockMvc.perform(
            MockMvcRequestBuilders.get("/actuator/enpoint").accept(APPLICATION_JSON)
        ).andExpect(status().isOk());
    }
Naresh Joshi
  • 4,188
  • 35
  • 45
0

Since now info endpoint must be enabled manually make sure the SpringBootTest tag includes this in properties, like this:

@SpringBootTest(
        properties = {
                "management.info.env.enabled=true" ,
                "management.endpoints.web.exposure.include=info, health"
        })
0

I had this problem recently, and as none of the above answers made any sense to me, I decided to do a bit more reading. In my case, I had already defined both server.port and management.server.port as 8091 in my test application-test.yaml file, and could not understand why my test was getting a connection refused error message.

It turns out that instead of using the annotation @SpringBootTest() I needed to use @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT) - which causes the port numbers in the yaml file to be used. This is briefly discussed in the manual. Quoting the relevant section:

DEFINED_PORT — Loads an EmbeddedWebApplicationContext and provides a real servlet environment. Embedded servlet containers are started and listening on a defined port (i.e from your application.properties or on the default port 8080).

It seems in SpringBootTest the default is to avoid starting a real servlet environment, and if no WebEnvironment is explicitly specified then SpringBootTest.WebEnvironment.MOCK is used as a default.

Jon
  • 3,510
  • 6
  • 27
  • 32
0

After a long search: There is this nice Springboot annotation called @LocalManagementPort!

It works similar to @LocalServerPort but for actuator endpoins.

An example config would look as follows

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class MetricsIT {

    @Autowired
    RestTemplateBuilder restTemplateBuilder;

    @LocalManagementPort
    int managementPort;

    @Test
    public void testMetrics(){
        ResponseEntity<String> response = restTemplateBuilder
            .rootUri("http://localhost:" + managementPort + "/actuator")
            .build().exchange("/metrics", HttpMethod.GET, new HttpEntity<>(null), String.class);
    }
}
Wouter
  • 192
  • 12
  • This one failed for me with error ``` org.springframework.web.client.HttpClientErrorException$NotFound: 404 : "{"timestamp":"2023-05-31T22:09:49.535+00:00","status":404,"error":"Not Found","path":"/actuator/metrics"}" ``` – gstackoverflow May 31 '23 at 22:11