Spring Boot - 模拟对外部 API 的 POST REST 请求

Posted

技术标签:

【中文标题】Spring Boot - 模拟对外部 API 的 POST REST 请求【英文标题】:Spring Boot - Mock a POST REST request to an external API 【发布时间】:2019-11-06 08:17:18 【问题描述】:

我有一个 Spring-Boot 1.5.21 应用程序,它充当 Angular UI 和提供数据的外部 API 之间的 REST 网关(长话短说 - 充当 UI 和数据源之间的身份验证)。请求到达 Spring-Boot 应用程序,它使用请求负载调用数据源 API。

我是 Spring-Boot 单元测试的新手,我正在尝试为网关应用程序中的 POST REST 方法编写测试,以创建新记录(创建)。我已经阅读了一些教程和其他网站,详细介绍了如何对 Spring-Boot API 进行单元测试,但对我的情况没有任何帮助。

我想:

对 REST 控制器方法进行单元测试并检查 @RequestBody 是否有效 我不想在数据源中创建记录

控制器方法:

@PostMapping(value = "/" + Constants.API_CHANGE_REQUEST, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
public String submitChangeRequest(@RequestBody ChangeRequestWrapper changeRequestWrapper) 
    logger.info("API Request: Posting Change Request: " + changeRequestWrapper.toString());
    return restService.makeApiPost(sharedDataService.buildApiUrlPath(Constants.API_CHANGE_REQUEST), changeRequestWrapper);

应用配置:

@PropertySource("classpath:application.properties")
@Configuration
public class AppConfig 

    @Resource
    private Environment env;

    @Bean
    public RestTemplate restTemplate() 
        RestTemplateBuilder builder = new RestTemplateBuilder();
        return builder
                .setConnectTimeout(Constants.API_TIMEOUT_CONNECT)
                .setReadTimeout(Constants.API_TIMEOUT_READ)
                .basicAuthorization(env.getProperty("bpm.user"), env.getProperty("bpm.password"))
                .build();
    

RestServiceImpl:

@Service
public class RestServiceImpl implements RestService 

    private static final Logger logger = LoggerFactory.getLogger(RestServiceImpl.class);

    @Autowired
    private RestTemplate myRestTemplate;

    @Value("$bpm.url")
    private String restUrl;

    public String getApiUri() 
        return restUrl;
    

    public String makeApiCall(String payload) /*throws GradeAdminException */
        logger.info("Implementing API call.");
        logger.debug("userApi: " + payload);
        return myRestTemplate.getForObject(payload, String.class);
    

    public String makeApiPost(String endpoint, Object object) 
        logger.info("Implementing API post submission");
        logger.debug("userApi endpoint: " + endpoint);
        return myRestTemplate.postForObject(endpoint, object, String.class);
    

SharedDataServiceImpl:

@Service
public class SharedDataServiceImpl implements SharedDataService 

    @Autowired
    private RestService restService;

    @Override
    public String buildApiUrlPath(String request) 
        return buildApiUrlPath(request, null);
    

    @Override
    public String buildApiUrlPath(String request, Object parameter) 
        String path;
        UriComponentsBuilder builder = UriComponentsBuilder.fromUriString(restService.getApiUri());

        if (parameter != null) 
            builder = builder.path(getApiPath(request) + "/object");
            UriComponents buildPath = builder.buildAndExpand(parameter);
            path = buildPath.toUriString();
         else 
            builder = builder.path(getApiPath(request));
            path = builder.build().toUriString();
        

        return path;
    

我为 GET 方法做了什么:

@RunWith(SpringRunner.class)
@WebMvcTest(ClientDataRequestController.class)
@ContextConfiguration(classes =  TestConfig.class , loader = AnnotationConfigWebContextLoader.class)
public class ClientDataRequestControllerTest 

    @Autowired
    private MockMvc mvc;

    @Before
    public void setUp() 

    

    @Test
    public void test_no_endpoint() throws Exception 
        this.mvc.perform(get("/")).andExpect(status().isNotFound()).andReturn();
    

    @Test
    public void test_controller_no_endpoint() throws Exception 
        this.mvc.perform(get("/api/")).andExpect(status().isOk()).andReturn();
    

    @Test
    public void test_getStudent_valid_parameters() throws Exception 
        this.mvc.perform(get("/api/students/?pidm=272746")).andExpect(status().isOk()).andReturn();
    

我将不胜感激。

解决方案:

我后来发现this SO answer 解决了我的问题。

【问题讨论】:

使用构造函数注入代替字段注入,然后注入RestOperations的mock而不是RestTemplate 【参考方案1】:

您可以模拟 RestServiceImpl。在您的测试中添加一个依赖项并使用 MockBean 对其进行注释:

@MockBean
private RemoteService remoteService;

现在你可以继续模拟这些方法了:

import org.mockito.BDDMockito;


BDDMockito.given(this.remoteService.makeApiPost()).willReturn("whatever is needed for your test");

【讨论】:

谢谢。不完全是我所追求的,我后来发现了这个:***.com/questions/20504399/…

以上是关于Spring Boot - 模拟对外部 API 的 POST REST 请求的主要内容,如果未能解决你的问题,请参考以下文章

如何在Spring Boot中测试对外部api的调用

Spring Boot - 访问外部接口最全总结

Spring Boot 从外部 API 返回 JSON 字符串

如何从 Spring Boot 应用程序调用外部 JWT API 服务 [关闭]

如何在 Spring Boot 应用程序中从 Api 网关(Zuul)调用外部服务(非 MSA)

如何在 Spring Boot REST API 中为 db insert 方法编写模拟单元测试?