You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot下Mockito测试WebClient初始化失败问题求助

问题描述

我正在开发基于Java Spring Boot 2.x和Java 1.8的项目,依赖JUnit 4.13.2与Mockito 3.12.4。编写StarshipService测试时遇到以下问题:

  • 最初@Before注解的setup方法无法执行(调试无法命中断点),导致webClientBuilder为null,初始化StarshipService时抛出InjectMocksException,报错信息为:无法调用WebClient.Builder.build(),因为baseUrl返回值为null。
  • 后续调整后能进入setup方法,但webClientBuilder仍为null,调用when()方法时程序崩溃。

项目中已通过AppConfig配置WebClient.Builder Bean,StarshipService通过构造注入该Builder并初始化WebClient。


服务类代码

@Service
public class StarshipService {

    private final WebClient webClient;

    @Autowired
    public StarshipService(WebClient.Builder webClientBuilder) {
        this.webClient = webClientBuilder.baseUrl("https://www.swapi.tech/api").build();
    }

    public StarshipResponse getStarships(int page, int size, String nameOrId){
        try {

            // 如果nameOrId是数字,按ID查询
            if (nameOrId != null && nameOrId.matches("\\d+")) {
                return getStarshipById(nameOrId);
            }

            UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl("https://www.swapi.tech/api/starships");

            if (page != 0){
                builder.queryParam("page", page);
            }
            builder.queryParam("limit", size);
            if (nameOrId != null && !nameOrId.isEmpty()) {
                builder.queryParam("name", nameOrId);
            }

            String url = builder.toUriString();

            Mono<ResponseEntity<String>> responseMono = this.webClient.get()
                    .uri(url)
                    .retrieve()
                    .toEntity(String.class);

            ResponseEntity<String> responseEntity = responseMono.block();

            if (responseEntity != null && responseEntity.getStatusCode().is2xxSuccessful()) {
                String responseBody = responseEntity.getBody();
                if (responseBody != null && responseBody.contains("results")) {
                    return StarWarsParseService.parseStandardStarshipResponse(responseBody);
                } else if (responseBody != null && responseBody.contains("result")) {
                    return StarWarsParseService.parseSearchStarshipResponse(responseBody);
                }
            }
            return new StarshipResponse();
        } catch (HttpClientErrorException e){
            System.err.println("调用Star Wars API出错: " + e.getMessage());
            e.printStackTrace();
            return new StarshipResponse();
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }

    public StarshipResponse getStarshipById(String id) throws JsonProcessingException {
        try {
            String url = "https://www.swapi.tech/api/starships/" + id;

            Mono<ResponseEntity<String>> responseMono = this.webClient.get()
                    .uri(url)
                    .retrieve()
                    .toEntity(String.class);

            try{
                ResponseEntity<String> responseEntity = responseMono.block();
                if (responseEntity != null && responseEntity.getStatusCode().is2xxSuccessful()) {
                    return StarWarsParseService.parseIdStarshipResponse(responseEntity.getBody());
                } else {
                    System.err.println("意外响应或状态码: " + responseEntity);
                    return new StarshipResponse();
                }
            }catch (WebClientResponseException.NotFound ex){
                System.err.println("SWAPI中未找到ID为" + id + "的飞船");
                StarshipResponse starshipResponse = new StarshipResponse();
                starshipResponse.setResults(new ArrayList<>());
                starshipResponse.setTotal_records(0);
                starshipResponse.setTotal_pages(1);
                return starshipResponse;
            }
        } catch (HttpClientErrorException e) {
            System.err.println("调用Star Wars API出错: " + e.getMessage());
            e.printStackTrace();
            return new StarshipResponse();
        }
        catch (Exception e) {
            System.err.println("意外错误: " + e.getMessage());
            e.printStackTrace();
            return new StarshipResponse();
        }
    }
}

测试类代码

@RunWith(MockitoJUnitRunner.class)
@SpringBootTest
public class StarshipServiceTest {

    @Mock
    private WebClient.Builder webClientBuilder;

    @Mock
    private WebClient webClient;

    @Mock
    private WebClient.RequestHeadersUriSpec requestHeadersUriSpec;

    @Mock
    private WebClient.RequestHeadersSpec requestHeadersSpec;

    @Mock
    private WebClient.ResponseSpec responseSpec;

    @InjectMocks
    private StarshipService starshipService;

    @Before
    public void setUp() {
        MockitoAnnotations.initMocks(this);
        when(webClientBuilder.baseUrl(anyString())).thenReturn(webClientBuilder);
        when(webClientBuilder.build()).thenReturn(webClient);
    }

    @Test
    public void testGetStarships() throws JsonProcessingException {
        // 模拟WebClient行为
        when(webClient.get()).thenReturn(requestHeadersUriSpec);
        when(requestHeadersUriSpec.uri(anyString())).thenReturn(requestHeadersSpec);
        when(requestHeadersSpec.retrieve()).thenReturn(responseSpec);
        when(responseSpec.toEntity(String.class)).thenReturn(Mono.just(new ResponseEntity<>("{\"results\": []}", HttpStatus.OK)));

        // 调用测试方法
        StarshipResponse response = starshipService.getStarships(1, 10, null);

        // 断言响应
        assertNotNull(response);
        assertEquals(0, response.getResults().size());
    }

    @Test
    public void testGetStarshipById() throws JsonProcessingException {
        // 模拟WebClient行为
        when(webClient.get()).thenReturn(requestHeadersUriSpec);
        when(requestHeadersUriSpec.uri(anyString())).thenReturn(requestHeadersSpec);
        when(requestHeadersSpec.retrieve()).thenReturn(responseSpec);
        when(responseSpec.toEntity(String.class)).thenReturn(Mono.just(new ResponseEntity<>("{\"result\": {\"properties\": {\"name\": \"Millennium Falcon\"}}}", HttpStatus.OK)));

        // 调用测试方法
        StarshipResponse response = starshipService.getStarshipById("10");

        // 断言响应
        assertNotNull(response);
        assertEquals("Millennium Falcon", response.getResults().get(0).getName());
    }

    @Test
    public void testGetStarshipByIdNotFound() throws JsonProcessingException {
        // 模拟WebClient返回404 Not Found的行为
        when(webClient.get()).thenReturn(requestHeadersUriSpec);
        when(requestHeadersUriSpec.uri(anyString())).thenReturn(requestHeadersSpec);
        when(requestHeadersSpec.retrieve()).thenReturn(responseSpec);
        when(responseSpec.toEntity(String.class)).thenReturn(Mono.error(new WebClientResponseException(404, "Not Found", null, null, null)));

        // 调用测试方法
        StarshipResponse response = starshipService.getStarshipById("0");

        // 断言响应
        assertNotNull(response);
        assertEquals(0, response.getTotal_records());
    }
}

配置类代码

@Configuration
public class AppConfig {
    @Bean
    public WebClient.Builder webClientBuilder() {
        return WebClient.builder();
    }
}

内容的提问来源于stack exchange,提问作者Juan Federico Di Leo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 06:42:20