任何人都可以帮我解决我在尝试为 Springboot 中具有 JaergerTracer 的服务编写 Junit 时遇到的空指针异常吗? 我有一个 Spring Boot 服务,该服务使用 Jaeger 进行跨度..该服务工作正常,但 Junit 给出了 NPE,因为模拟的 JaegerTracer 给出了 null..
我使用过JUnit5和SpringBoot 2.7.6。
请求检查并提出建议..
服务
@Service
public class RestClientHeroMovieService {
@Autowired
private RestTemplate restTemplate;
@Autowired
private RestClientService restClientService;
@Autowired
private StatisticsConfig statisticsConfig;
@Autowired
private JaegerTracer jaegerTracer;
@Value("${test.value}")
private String testValue;
public HeroMovie getHeroMovieDetails(String heroName) throws URISyntaxException {
Span span = jaegerTracer.buildSpan("getHeroMovieDetails").start(); //giving NPE as jaegerTracer is null
Span spanHeroMovie = jaegerTracer.buildSpan("spanHeroMovie").asChildOf(span).start();
String endpoint = "http://localhost:9050/orders/v1/getHeroDetails/"+heroName;
HeroMovie heroMovie = restTemplate.getForObject(endpoint,HeroMovie.class);
spanHeroMovie.finish();
endpoint = "http://localhost:9050/orders/v1/getMoviesOfHero/"+heroName;
URI uri = new URI(endpoint);
Span spanListMovie = jaegerTracer.buildSpan("spanListMovie").asChildOf(span).start();
List<Movie> listMovie = restTemplate.exchange(uri, HttpMethod.GET, null, new ParameterizedTypeReference<List<Movie>>(){}).getBody();
heroMovie.setMovies(listMovie);
spanListMovie.finish();
heroMovie.setPlatforms(statisticsConfig.getPlatforms());
heroMovie.setBrowsers(statisticsConfig.getBrowsers());
heroMovie.setSystems(statisticsConfig.getSystems());
heroMovie.setSomeTestValue(testValue);
heroMovie.setPet(getPet(heroMovie.getHeroName()));
restClientService.clearAllCache();
span.finish();
return heroMovie;
}
JUnit
@ExtendWith({SpringExtension.class})
@ExtendWith({MockitoExtension.class})
@SpringBootTest(classes = {RestClientHeroMovieServiceTest.class, StatisticsConfig.class})
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
@EnableConfigurationProperties(value = {StatisticsConfig.class})
@TestPropertySource("classpath:test.properties")
public class RestClientHeroMovieServiceTest {
private final String testValue = "aaa";
@InjectMocks
@Spy
private RestClientHeroMovieService restClientHeroMovieService;
@Mock
private RestTemplate restTemplate;
@Mock
private RestClientService restClientService;
@Mock
private StatisticsConfig statisticsConfig;
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private JaegerTracer jaegerTracer;
@BeforeEach
public void init() {
ReflectionTestUtils.setField(restClientHeroMovieService, "testValue", testValue);
MockitoAnnotations.openMocks(this);
}
@Test
@SneakyThrows
public void testGetHeroMovieDetails() {
HeroMovie heroMovie = getHeroMovie();
restClientHeroMovieService.setJaegerTracer(jaegerTracer);
Mockito.when(jaegerTracer.buildSpan(Mockito.anyString()).start())
.thenReturn(Mockito.mock(JaegerSpan.class));
Mockito.doReturn(heroMovie)
.when(restTemplate)
.getForObject(Mockito.anyString(), Mockito.any());
List<Movie> listMovie = getListMovie();
ResponseEntity<List<Movie>> responseEntityListMovie = ResponseEntity.ok(listMovie);
Mockito.doReturn(responseEntityListMovie)
.when(restTemplate)
.exchange(Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any(ParameterizedTypeReference.class));
Mockito.doNothing()
.when(restClientService)
.clearAllCache();
Assertions.assertTrue(heroMovie.getHeroName().equalsIgnoreCase("test") && heroMovie.getMovies().get(0).getMovieName().equalsIgnoreCase("TestM") && heroMovie.getSomeTestValue().equalsIgnoreCase(testValue));
}
JaegerTracer
是一个实现 io.opentracing.Tracer
接口的类。你的类应该自动装配一个 Tracer
bean。
Mockito 仍然必须遵循 JVM 的规则,这意味着诸如以下的语句:
Mockito.when(jaegerTracer.buildSpan(Mockito.anyString()).start())
.thenReturn(Mockito.mock(JaegerSpan.class));
在进入 buildSpan
方法之前,
必须仍然调用
start
和
when
(Java 中的参数会被急切求值)。
最后,不要将 Mockito 测试与 Spring 测试混合在一起;它们的注释不能很好地混合,这很可能就是自动装配字段为空的原因。决定一种方法并仅使用一种:要么在应用程序上下文中使用 Spring 注册 bean(和模拟 bean;用
@MockBean
定义)并自动装配它们,要么使用 Mockito 创建模拟实例并注入它们(用 @Mock
和 @InjectMocks
)。