Skip to content

Commit 697a2ac

Browse files
committed
Remove reflection to make tests more stable
See spring-projectsgh-29666
1 parent eacfe42 commit 697a2ac

File tree

2 files changed

+98
-130
lines changed

2 files changed

+98
-130
lines changed

spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/ObservationAutoConfigurationTests.java

+94-58
Original file line numberDiff line numberDiff line change
@@ -16,23 +16,22 @@
1616

1717
package org.springframework.boot.actuate.autoconfigure.metrics;
1818

19-
import java.lang.reflect.Field;
20-
import java.lang.reflect.Method;
21-
import java.util.Collection;
19+
import java.util.ArrayList;
2220
import java.util.List;
2321

22+
import io.micrometer.core.instrument.MeterRegistry;
23+
import io.micrometer.core.instrument.Tag;
2424
import io.micrometer.core.instrument.Tags;
2525
import io.micrometer.core.instrument.observation.MeterObservationHandler;
26+
import io.micrometer.core.instrument.observation.Observation;
2627
import io.micrometer.core.instrument.observation.Observation.Context;
2728
import io.micrometer.core.instrument.observation.Observation.GlobalTagsProvider;
2829
import io.micrometer.core.instrument.observation.ObservationHandler;
2930
import io.micrometer.core.instrument.observation.ObservationHandler.AllMatchingCompositeObservationHandler;
30-
import io.micrometer.core.instrument.observation.ObservationHandler.CompositeObservationHandler;
3131
import io.micrometer.core.instrument.observation.ObservationHandler.FirstMatchingCompositeObservationHandler;
3232
import io.micrometer.core.instrument.observation.ObservationPredicate;
3333
import io.micrometer.core.instrument.observation.ObservationRegistry;
34-
import io.micrometer.core.instrument.observation.ObservationRegistry.ObservationConfig;
35-
import io.micrometer.core.instrument.observation.TimerObservationHandler;
34+
import io.micrometer.core.instrument.search.MeterNotFoundException;
3635
import io.micrometer.tracing.handler.TracingObservationHandler;
3736
import org.junit.jupiter.api.Test;
3837

@@ -42,10 +41,11 @@
4241
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
4342
import org.springframework.context.annotation.Bean;
4443
import org.springframework.context.annotation.Configuration;
44+
import org.springframework.context.annotation.Import;
4545
import org.springframework.core.annotation.Order;
46-
import org.springframework.util.ReflectionUtils;
4746

4847
import static org.assertj.core.api.Assertions.assertThat;
48+
import static org.assertj.core.api.Assertions.assertThatThrownBy;
4949

5050
/**
5151
* Tests for {@link ObservationAutoConfiguration}.
@@ -59,75 +59,57 @@ class ObservationAutoConfigurationTests {
5959
.withConfiguration(AutoConfigurations.of(ObservationAutoConfiguration.class));
6060

6161
@Test
62-
void autoConfiguresTimerTimerObservationHandler() {
62+
void autoConfiguresTimerObservationHandler() {
6363
this.contextRunner.run((context) -> {
64-
ObservationRegistry observationRegistry = context.getBean(ObservationRegistry.class);
65-
List<ObservationHandler<?>> handlers = getObservationHandlers(observationRegistry);
66-
assertThat(handlers).hasSize(1);
67-
assertThat(handlers.get(0)).isInstanceOf(TimerObservationHandler.class);
68-
64+
MeterRegistry meterRegistry = context.getBean(MeterRegistry.class);
65+
Observation.start("test-observation", meterRegistry).stop();
66+
// When a TimerObservationHandler is registered, every stopped Observation
67+
// leads to a timer
68+
assertThat(meterRegistry.get("test-observation").timer().count()).isEqualTo(1);
6969
});
7070
}
7171

7272
@Test
7373
void autoConfiguresObservationPredicates() {
7474
this.contextRunner.withUserConfiguration(ObservationPredicates.class).run((context) -> {
75-
ObservationRegistry observationRegistry = context.getBean(ObservationRegistry.class);
76-
assertThat(getObservationPredicates(observationRegistry)).hasSize(1);
75+
MeterRegistry meterRegistry = context.getBean(MeterRegistry.class);
76+
// This is allowed by ObservationPredicates.customPredicate
77+
Observation.start("observation1", meterRegistry).start().stop();
78+
// This isn't allowed by ObservationPredicates.customPredicate
79+
Observation.start("observation2", meterRegistry).start().stop();
80+
assertThat(meterRegistry.get("observation1").timer().count()).isEqualTo(1);
81+
assertThatThrownBy(() -> meterRegistry.get("observation2").timer())
82+
.isInstanceOf(MeterNotFoundException.class);
7783
});
7884
}
7985

8086
@Test
8187
void autoConfiguresGlobalTagsProvider() {
8288
this.contextRunner.withUserConfiguration(GlobalTagsProviders.class).run((context) -> {
8389
ObservationRegistry observationRegistry = context.getBean(ObservationRegistry.class);
84-
assertThat(getTagsProviders(observationRegistry)).hasSize(1);
90+
Context micrometerContext = new Context();
91+
Observation.start("test-observation", micrometerContext, observationRegistry).stop();
92+
assertThat(micrometerContext.getAllTags()).containsExactly(Tag.of("tag1", "value1"));
8593
});
8694
}
8795

8896
@Test
89-
@SuppressWarnings("rawtypes")
9097
void autoConfiguresObservationHandler() {
9198
this.contextRunner.withUserConfiguration(ObservationHandlers.class).run((context) -> {
9299
ObservationRegistry observationRegistry = context.getBean(ObservationRegistry.class);
93-
List<ObservationHandler<?>> handlers = getObservationHandlers(observationRegistry);
94-
assertThat(handlers).hasSize(5);
95-
assertThat(handlers.get(0)).isInstanceOf(TimerObservationHandler.class);
96-
assertThat(handlers.get(1)).isInstanceOf(CustomObservationHandler.class);
97-
assertThat(handlers.get(2)).isInstanceOf(FirstMatchingCompositeObservationHandler.class);
98-
assertThat(handlers.get(3)).isInstanceOf(AllMatchingCompositeObservationHandler.class);
99-
// CustomMeterObservationHandlers get wrapped into a
100-
// FirstMatchingCompositeObservationHandler
101-
assertThat(handlers.get(4)).isInstanceOf(FirstMatchingCompositeObservationHandler.class);
102-
List<ObservationHandler> containedHandlers = ((CompositeObservationHandler) handlers.get(4)).getHandlers();
103-
assertThat(containedHandlers).hasSize(2);
104-
assertThat(containedHandlers.get(0)).isInstanceOf(CustomMeterObservationHandler.class);
105-
assertThat(containedHandlers.get(1)).isInstanceOf(CustomMeterObservationHandler.class);
100+
List<ObservationHandler<?>> handlers = context.getBean(CalledHandlers.class).getCalledHandlers();
101+
Observation.start("test-observation", observationRegistry);
102+
assertThat(handlers).hasSize(2);
103+
// Regular handlers are registered first
104+
assertThat(handlers.get(0)).isInstanceOf(CustomObservationHandler.class);
105+
// Multiple MeterObservationHandler are wrapped in
106+
// FirstMatchingCompositeObservationHandler, which calls only the first one
107+
assertThat(handlers.get(1)).isInstanceOf(CustomMeterObservationHandler.class);
108+
assertThat(((CustomMeterObservationHandler) handlers.get(1)).getName())
109+
.isEqualTo("customMeterObservationHandler1");
106110
});
107111
}
108112

109-
@SuppressWarnings("unchecked")
110-
private List<ObservationPredicate> getObservationPredicates(ObservationRegistry observationRegistry) {
111-
Field field = ReflectionUtils.findField(ObservationConfig.class, "observationPredicates");
112-
ReflectionUtils.makeAccessible(field);
113-
return (List<ObservationPredicate>) ReflectionUtils.getField(field, observationRegistry.observationConfig());
114-
}
115-
116-
@SuppressWarnings("unchecked")
117-
private Collection<GlobalTagsProvider<?>> getTagsProviders(ObservationRegistry registry) {
118-
Method method = ReflectionUtils.findMethod(ObservationConfig.class, "getTagsProviders");
119-
ReflectionUtils.makeAccessible(method);
120-
return (Collection<GlobalTagsProvider<?>>) ReflectionUtils.invokeMethod(method, registry.observationConfig());
121-
}
122-
123-
@SuppressWarnings("unchecked")
124-
private List<ObservationHandler<?>> getObservationHandlers(ObservationRegistry registry) {
125-
Method method = ReflectionUtils.findMethod(ObservationConfig.class, "getObservationHandlers");
126-
ReflectionUtils.makeAccessible(method);
127-
return List.copyOf(
128-
(Collection<ObservationHandler<?>>) ReflectionUtils.invokeMethod(method, registry.observationConfig()));
129-
}
130-
131113
@Configuration(proxyBeanMethods = false)
132114
static class ObservationPredicates {
133115

@@ -159,6 +141,17 @@ public Tags getLowCardinalityTags(Context context) {
159141
}
160142

161143
@Configuration(proxyBeanMethods = false)
144+
static class CalledHandlersConfiguration {
145+
146+
@Bean
147+
CalledHandlers calledHandlers() {
148+
return new CalledHandlers();
149+
}
150+
151+
}
152+
153+
@Configuration(proxyBeanMethods = false)
154+
@Import(CalledHandlersConfiguration.class)
162155
static class ObservationHandlers {
163156

164157
@Bean
@@ -175,26 +168,51 @@ FirstMatchingCompositeObservationHandler customFirstMatchingCompositeObservation
175168

176169
@Bean
177170
@Order(2)
178-
ObservationHandler<Context> customObservationHandler() {
179-
return new CustomObservationHandler();
171+
ObservationHandler<Context> customObservationHandler(CalledHandlers calledHandlers) {
172+
return new CustomObservationHandler(calledHandlers);
180173
}
181174

182175
@Bean
183176
@Order(1)
184-
MeterObservationHandler<Context> customMeterObservationHandler2() {
185-
return new CustomMeterObservationHandler();
177+
MeterObservationHandler<Context> customMeterObservationHandler2(CalledHandlers calledHandlers) {
178+
return new CustomMeterObservationHandler("customMeterObservationHandler2", calledHandlers);
186179
}
187180

188181
@Bean
189182
@Order(0)
190-
MeterObservationHandler<Context> customMeterObservationHandler1() {
191-
return new CustomMeterObservationHandler();
183+
MeterObservationHandler<Context> customMeterObservationHandler1(CalledHandlers calledHandlers) {
184+
return new CustomMeterObservationHandler("customMeterObservationHandler1", calledHandlers);
185+
}
186+
187+
}
188+
189+
static class CalledHandlers {
190+
191+
private final List<ObservationHandler<?>> calledHandlers = new ArrayList<>();
192+
193+
void onCalled(ObservationHandler<?> handler) {
194+
this.calledHandlers.add(handler);
195+
}
196+
197+
List<ObservationHandler<?>> getCalledHandlers() {
198+
return this.calledHandlers;
192199
}
193200

194201
}
195202

196203
static class CustomObservationHandler implements ObservationHandler<Context> {
197204

205+
private final CalledHandlers calledHandlers;
206+
207+
CustomObservationHandler(CalledHandlers calledHandlers) {
208+
this.calledHandlers = calledHandlers;
209+
}
210+
211+
@Override
212+
public void onStart(Context context) {
213+
this.calledHandlers.onCalled(this);
214+
}
215+
198216
@Override
199217
public boolean supportsContext(Context context) {
200218
return true;
@@ -204,6 +222,24 @@ public boolean supportsContext(Context context) {
204222

205223
static class CustomMeterObservationHandler implements MeterObservationHandler<Context> {
206224

225+
private final CalledHandlers calledHandlers;
226+
227+
private final String name;
228+
229+
CustomMeterObservationHandler(String name, CalledHandlers calledHandlers) {
230+
this.name = name;
231+
this.calledHandlers = calledHandlers;
232+
}
233+
234+
String getName() {
235+
return this.name;
236+
}
237+
238+
@Override
239+
public void onStart(Context context) {
240+
this.calledHandlers.onCalled(this);
241+
}
242+
207243
@Override
208244
public boolean supportsContext(Context context) {
209245
return true;

spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/TracingAutoConfigurationTests.java

+4-72
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616

1717
package org.springframework.boot.actuate.autoconfigure.metrics;
1818

19-
import java.util.ArrayList;
2019
import java.util.List;
2120

2221
import io.micrometer.core.instrument.observation.MeterObservationHandler;
@@ -32,6 +31,10 @@
3231
import org.mockito.Answers;
3332
import org.mockito.Mockito;
3433

34+
import org.springframework.boot.actuate.autoconfigure.metrics.ObservationAutoConfigurationTests.CalledHandlers;
35+
import org.springframework.boot.actuate.autoconfigure.metrics.ObservationAutoConfigurationTests.CalledHandlersConfiguration;
36+
import org.springframework.boot.actuate.autoconfigure.metrics.ObservationAutoConfigurationTests.CustomMeterObservationHandler;
37+
import org.springframework.boot.actuate.autoconfigure.metrics.ObservationAutoConfigurationTests.CustomObservationHandler;
3538
import org.springframework.boot.actuate.autoconfigure.metrics.test.MetricsRun;
3639
import org.springframework.boot.autoconfigure.AutoConfigurations;
3740
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
@@ -74,16 +77,6 @@ void autoConfiguresObservationHandler() {
7477
});
7578
}
7679

77-
@Configuration(proxyBeanMethods = false)
78-
static class CalledHandlersConfiguration {
79-
80-
@Bean
81-
CalledHandlers calledHandlers() {
82-
return new CalledHandlers();
83-
}
84-
85-
}
86-
8780
@Configuration(proxyBeanMethods = false)
8881
@Import(CalledHandlersConfiguration.class)
8982
static class ObservationHandlers {
@@ -132,20 +125,6 @@ MeterObservationHandler<Context> customMeterObservationHandler1(CalledHandlers c
132125

133126
}
134127

135-
private static class CalledHandlers {
136-
137-
private final List<ObservationHandler<?>> calledHandlers = new ArrayList<>();
138-
139-
void onCalled(ObservationHandler<?> handler) {
140-
this.calledHandlers.add(handler);
141-
}
142-
143-
List<ObservationHandler<?>> getCalledHandlers() {
144-
return this.calledHandlers;
145-
}
146-
147-
}
148-
149128
private static class CustomTracingObservationHandler implements TracingObservationHandler<Context> {
150129

151130
private final Tracer tracer = Mockito.mock(Tracer.class, Answers.RETURNS_MOCKS);
@@ -180,51 +159,4 @@ public boolean supportsContext(Context context) {
180159

181160
}
182161

183-
private static class CustomObservationHandler implements ObservationHandler<Context> {
184-
185-
private final CalledHandlers calledHandlers;
186-
187-
CustomObservationHandler(CalledHandlers calledHandlers) {
188-
this.calledHandlers = calledHandlers;
189-
}
190-
191-
@Override
192-
public void onStart(Context context) {
193-
this.calledHandlers.onCalled(this);
194-
}
195-
196-
@Override
197-
public boolean supportsContext(Context context) {
198-
return true;
199-
}
200-
201-
}
202-
203-
private static class CustomMeterObservationHandler implements MeterObservationHandler<Context> {
204-
205-
private final CalledHandlers calledHandlers;
206-
207-
private final String name;
208-
209-
CustomMeterObservationHandler(String name, CalledHandlers calledHandlers) {
210-
this.name = name;
211-
this.calledHandlers = calledHandlers;
212-
}
213-
214-
String getName() {
215-
return this.name;
216-
}
217-
218-
@Override
219-
public void onStart(Context context) {
220-
this.calledHandlers.onCalled(this);
221-
}
222-
223-
@Override
224-
public boolean supportsContext(Context context) {
225-
return true;
226-
}
227-
228-
}
229-
230162
}

0 commit comments

Comments
 (0)