我无法在使用mockito的测试中初始化我的网络客户端

问题描述 投票:0回答:1

我正在开发一个使用 java spring boot 2.x 和 java 1.8 的项目。在我的pom中,我得到了junit 4.13.2和mockito 3.12.4的依赖项。当我尝试运行测试时,我遇到的问题是,即使使用 @before 注释,我的设置功能也没有运行,因此 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 {

            // Si nameOrId es un número, busca por 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("Error al llamar a la API de Star Wars: " + 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("Unexpected response or status code: " + responseEntity);
                    return new StarshipResponse();
                }
            }catch (WebClientResponseException.NotFound ex){
                System.err.println("Person with ID " + id + " not found in SWAPI");
                StarshipResponse starshipResponse = new StarshipResponse();
                starshipResponse.setResults(new ArrayList<>());
                starshipResponse.setTotal_records(0);
                starshipResponse.setTotal_pages(1);
                return starshipResponse;
            }
        } catch (HttpClientErrorException e) {
            System.err.println("Error al llamar a la API de Star Wars: " + e.getMessage());
            e.printStackTrace();
            return new StarshipResponse();
        }
        catch (Exception e) {
            System.err.println("Unexpected error: " + e.getMessage());
            e.printStackTrace();
            return new StarshipResponse();
        }
    }
}

以及使用mockito和junit进行的测试

@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 {
        // Mock the WebClient behavior
        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)));

        // Call the method to test
        StarshipResponse response = starshipService.getStarships(1, 10, null);

        // Assert the response
        assertNotNull(response);
        assertEquals(0, response.getResults().size());
    }

    @Test
    public void testGetStarshipById() throws JsonProcessingException {
        // Mock the WebClient behavior
        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)));

        // Call the method to test
        StarshipResponse response = starshipService.getStarshipById("10");

        // Assert the response
        assertNotNull(response);
        assertEquals("Millennium Falcon", response.getResults().get(0).getName());
    }

    @Test
    public void testGetStarshipByIdNotFound() throws JsonProcessingException {
        // Mock the WebClient behavior for 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)));

        // Call the method to test
        StarshipResponse response = starshipService.getStarshipById("0");

        // Assert the response
        assertNotNull(response);
        assertEquals(0, response.getTotal_records());
    }
}

但是当我尝试运行测试时,我收到了此错误

org.mockito.exceptions.misusing.InjectMocksException: 无法实例化类型为“class com.example.conexatest.conexatest.service.StarshipService”的名为“starshipService”的 @InjectMocks 字段。 您尚未在字段声明中提供实例,因此我尝试构造该实例。 然而,构造函数或初始化块抛出异常:无法调用“org.springframework.web.reactive.function.client.WebClient$Builder.build()”,因为“org.springframework.web.reactive.function.”的返回值。 client.WebClient$Builder.baseUrl(String)”为空

当我在设置函数中放置断点时,我在调试时无法到达它

我已经修复了一些问题,现在我可以进入设置函数,但 webClientBuilder 为空,当我调用 when() 函数时,程序崩溃。在我的项目中,我有这样的配置

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

服务正在这样消费:

private final WebClient webClient;

    @Autowired
    public StarshipService(WebClient.Builder webClientBuilder) {
        this.webClient = webClientBuilder.baseUrl("https://www.swapi.tech/api").build();
}
java spring spring-mvc junit mockito
1个回答
0
投票

您的构造函数从传递的 webClientBuilder 构建一个 WebClient,但此时构建器上尚未存根任何方法,因此

baseUrl(…)
将仅返回返回类型的默认值(即
null
)。

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

首先,Mockito Runner 会初始化 Mock 并将它们分配给字段,然后才会执行 setup 方法。

在您的情况下,您需要手动构建服务实例并传递存根模拟对象:

@Mock
private WebClient.ResponseSpec responseSpec;

private StarshipService starshipService; // assigned in setup

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

    this.starshipService = new StarshipService(webClientBuilder);
}

您还想删除所有不必要的注释和语句:

  • @SpringBootTest
    :您没有使用任何 Spring bean,也没有使用自动装配等 Spring 功能,那么为什么要加载应用程序上下文呢?
  • MockitoAnnotations.initMocks(this);
    :您已经使用 MockitoJUnitRunner 运行测试,因此
    initMocks
    将初始化 new 模拟实例并重新分配所有字段(已经通过
    @Mock
    @InjectMocks
    分配了不同的(模拟)实例)。
© www.soinside.com 2019 - 2024. All rights reserved.