Java 使用 RestTemplate 进行 RESTful 服务测试

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/31739939/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 11:32:37  来源:igfitidea点击:

RESTful Services test with RestTemplate

javarestjunitspring-boot

提问by quma

In my application I have a lot of REST- Services. I have written tests for all services with:

在我的应用程序中,我有很多 REST 服务。我已经为所有服务编写了测试:

org.springframework.web.client.RestTemplate

A REST- Service invokation e.g. looks like this:

REST- 服务调用例如如下所示:

final String loginResponse = restTemplate.exchange("http://localhost:8080/api/v1/xy", HttpMethod.POST, httpEntity, String.class)
        .getBody();

and I check the response body afterwards - all works fine. The disadvantage is, that the application for sure must be started in order to invoke the REST- Services.

然后我检查响应正文 - 一切正常。缺点是,必须启动应用程序才能调用 REST 服务。

My question now would be how I can do this in my JUnit- @Test methods? It is a Spring Boot application (with embedded tomcat).

我现在的问题是如何在我的 JUnit-@Test 方法中做到这一点?它是一个 Spring Boot 应用程序(带有嵌入式 tomcat)。

Thanks for help!

感谢帮助!

采纳答案by Raniz

There's a good chapteron this in the documentation, I suggest you read through it to fully understand what you can do.

文档中有一个很好的章节,我建议您通读一遍以完全了解您可以做什么。

I like to use @IntegrationTestwith a custom configuration since that starts up the entire server and lets you test the complete system. If you want to replace certain parts of the system with mocks you can do that by excluding certain configurations or beans and replacing them with your own.

我喜欢使用@IntegrationTest自定义配置,因为它会启动整个服务器并让您测试整个系统。如果你想用模拟替换系统的某些部分,你可以通过排除某些配置或 bean 并用你自己的替换它们来实现。

Here's a small example. I've left out the MessageServiceinterface because it's obvious from IndexControllerwhat it does, and it's default implementation - DefaultMessageService- because it's not relevant.

这是一个小例子。我省略了MessageService接口,因为它的作用很明显IndexController,而且它是默认实现 - DefaultMessageService- 因为它不相关。

What it does is that it spins up the entire application minus the DefaultMessageServicebut with it's own MessageServiceinstead. It then uses RestTemplateto issue real HTTP requests to the running application in the test case.

它的作用是启动整个应用程序减去DefaultMessageService但它自己的应用程序MessageService。然后它用于RestTemplate向测试用例中正在运行的应用程序发出真实的 HTTP 请求。

Application classes:

应用类:

IntegrationTestDemo.java:

IntegrationTestDemo.java:

@SpringBootApplication
public class IntegrationTestDemo {

    public static void main(String[] args) {
        SpringApplication.run(IntegrationTestDemo.class, args);
    }

}

IndexController.java:

索引控制器.java:

@RestController
public class IndexController {

    @Autowired
    MessageService messageService;

    @RequestMapping("/")
    String getMessage() {
        return messageService.getMessage();
    }
}

Test classes:

测试类:

IntegrationTestDemoTest.java:

IntegrationTestDemoTest.java:

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = TestConfig.class)
@WebIntegrationTest // This will start the server on a random port
public class IntegrationTestDemoTest {

    // This will hold the port number the server was started on
    @Value("${local.server.port}")
    int port;

    final RestTemplate template = new RestTemplate();

    @Test
    public void testGetMessage() {
        String message = template.getForObject("http://localhost:" + port + "/", String.class);

        Assert.assertEquals("This is a test message", message);
    }
}

TestConfig.java:

测试配置.java:

@SpringBootApplication
@ComponentScan(
    excludeFilters = {
        // Exclude the default message service
        @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = DefaultMessageService.class),
        // Exclude the default boot application or it's
        // @ComponentScan will pull in the default message service
        @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = IntegrationTestDemo.class)
    }
)
public class TestConfig {

    @Bean
    // Define our own test message service
    MessageService mockMessageService() {
        return new MessageService() {
            @Override
            public String getMessage() {
                return "This is a test message";
            }
        };
    }
}

回答by morsor

Since you are using Spring MVC for REST, I would recommend using the testing facilities supplied by instantiating MockMVC() - enabling tests such as:

由于您使用 Spring MVC for REST,我建议使用实例化 MockMVC() 提供的测试工具 - 启用测试,例如:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {
 ... // any required Spring config
)
@WebAppConfiguration
public class RestControllerTest {

    @Autowired
    private WebApplicationContext context;

    private MockMvc mockMvc;

    @Before
    public void setup() {
        mockMvc = MockMvcBuilders.webAppContextSetup(context).build();
    }


    @Test
    public void getUserList() throws Exception {
        mockMvc.perform(get("/user"))
            .andExpect(status().isOk())
            .andExpect(content().contentType("application/json;charset=UTF-8")) 
            .andExpect(content().encoding("UTF-8"))
            .andExpect(jsonPath("$", hasSize(8)))
            .andExpect(jsonPath("$[0].id").exists())
            .andExpect(jsonPath("$[0].alias").exists())
            .andExpect(jsonPath("$[0].name").exists())
        );
    }
}

This unit test will test a REST interface without deploying. Specifically, whether exactly 8 users are returned and the first one has the fields 'id', 'alias' and 'name'.

此单元测试将在不部署的情况下测试 REST 接口。具体来说,是否准确地返回了 8 个用户,第一个用户是否具有字段“id”、“alias”和“name”。

The jsonPath assertions require two dependencies:

jsonPath 断言需要两个依赖项:

'com.jayway.jsonpath:json-path:0.8.1'
'com.jayway.jsonpath:json-path-assert:0.8.1'

And probably also:

可能还有:

'org.springframework:spring-test:4.1.7.RELEASE'

回答by simomo

If you were not looking for a end to end (integretion) test, the MockRestServiceServermight help you. I found it's very useful to de-couple my test cases from a real service.

如果您不是在寻找端到端(集成)测试,这MockRestServiceServer可能对您有所帮助。我发现将我的测试用例与真实服务分离非常有用。

Spring doc said:

春天医生说:

Used for tests that involve direct or indirect use of the RestTemplate. Provides a way to set up expected requests that will be performed through the RestTemplate as well as mock responses to send back thus removing the need for an actual server.

用于涉及直接或间接使用 RestTemplate 的测试。提供一种方法来设置将通过 RestTemplate 执行的预期请求以及发送回的模拟响应,从而消除对实际服务器的需要

Here is the official doc

这是官方文档



One more tip is that, requestTocan not be imported automatically

还有一个提示是,requestTo不能自动导入

server.expect(manyTimes(), requestTo("/hotels/42")) ....

It's a static method of org.springframework.test.web.client.match.MockRestRequestMatchers

这是一个静态方法 org.springframework.test.web.client.match.MockRestRequestMatchers

回答by rieckpil

If you use Spring Boot, you can easily setup everything to test your RestTemplateif you annotate your test with @RestClientTest. This ensures to auto-configure the required parts (RestTemplateBuilder, ObjectMapper, MockRestServiceServer, etc.) of your application to test your client classes like e.g.:

如果你使用Spring启动,你可以轻松地设置一切来测试你的RestTemplate,如果你用注释您的测试@RestClientTest。这可确保自动配置所需的零件(RestTemplateBuilderObjectMapperMockRestServiceServer你的应用程序来测试像如您的客户端类等):

@Component
public class UserClient {

  private final RestTemplate restTemplate;

  public UserClient(RestTemplateBuilder restTemplateBuilder) {
    this.restTemplate = restTemplateBuilder.rootUri("https://reqres.in").build();
  }

  public User getSingleUser(Long id) {
    HttpHeaders headers = new HttpHeaders();
    headers.setContentType(MediaType.APPLICATION_JSON);

    HttpEntity<Void> requestEntity = new HttpEntity<>(headers);

    return this.restTemplate
      .exchange("/api/users/{id}", HttpMethod.GET, requestEntity, User.class, id)
      .getBody();

  }
}

The corresponding test (using JUnit 5) looks like the following:

相应的测试(使用 JUnit 5)如下所示:

@RestClientTest(UserClient.class)
class UserClientTest {

  @Autowired
  private UserClient userClient;

  @Autowired
  private ObjectMapper objectMapper;

  @Autowired
  private MockRestServiceServer mockRestServiceServer;

  @Test
  public void userClientSuccessfullyReturnsUserDuke() throws Exception {

    String json = this.objectMapper
      .writeValueAsString(new User(new UserData(42L, "[email protected]", "duke", "duke", "duke")));

    this.mockRestServiceServer
      .expect(requestTo("/api/users/42"))
      .andRespond(withSuccess(json, MediaType.APPLICATION_JSON));

    User result = userClient.getSingleUser(42L);

    assertEquals(42L, result.getData().getId());
    assertEquals("duke", result.getData().getFirstName());
    assertEquals("duke", result.getData().getLastName());
    assertEquals("duke", result.getData().getAvatar());
    assertEquals("[email protected]", result.getData().getEmail());
  }

}

This setup allows you to specify stub HTTP responses using MockRestServiceServer.

此设置允许您使用MockRestServiceServer.

I've provided a more detailed tutorialfor this, if wan to learn more about it.

如果想要了解更多信息,我已经为此提供了更详细的教程