在Spring中测试从控制器调用休息控制器

时间:2015-10-08 06:47:12

标签: spring rest unit-testing testing

我正在使用RestTemplate从控制器调用一个休息控制器,例如:

@RequestMapping(method = POST, consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    @ResponseBody
    public String formForUpload(@RequestParam("file") MultipartFile file) throws Exception {
    final MultiValueMap<String, Object> parts=...;
    restTemplate.postForObject("http://localhost:8080/rest/something",
                        parts, MultipartFile.class);
...
}

如何单元测试控制器调用的URL是否正确。有没有办法得到其余控制器的路径(例如使用这种反射技术)?

我不想进行集成测试!

3 个答案:

答案 0 :(得分:2)

也许这不是正确答案,但也许有帮助。

假设其余控制器(假设MyRestController)具有用@RequestMapping注释的类声明,那么我假设您可以简单地获取当前定义的路径,如下所示:

final String controllerPath = MyRestController.class
                    .getAnnotation(RequestMapping.class).value()[0];

<强>更新 为什么不尝试对代码进行以下更改。它们基于Spring,JUnit和Mockito。假设您的控制器是类MyController

@Test
    public void testUploadPostCallsToRestService() throws Exception {
        final MockMultipartFile file = //create a mock for a file to be uploaded;

        final MyController myController = new MyController();

        final RestTemplate restTemplate = new RestTemplate();
        final MockRestServiceServer mockServer = MockRestServiceServer.createServer(restTemplate);

        ReflectionTestUtils.setField(myController, "restTemplate", restTemplate); //I assume you use Spring MVC from your tagging, isn't it?

        final MockMvc mockMvc = MockMvcBuilders.standaloneSetup(myController).build();
mockServer.expect(requestTo("http://localhost:8080/rest/something"))
                .andExpect(method(POST))
                .andRespond(withStatus(OK)); //Or whatever your rest returns

        mockMvc.perform(fileUpload("http://localhost:8080/path/to/my/controller")
                .file(file)
                .accept(MediaType.MULTIPART_FORM_DATA))
                .andExpect(status().isOk()); //I hope you return this :)

        mockServer.verify();
    }

这对我很有用。基本上,我将模拟用于必须为RestTemplate提供服务的其他服务服务器。因此,后者将调用与您指定的行为相同的模拟。然后,使用反射将其注入控制器。最后,我使用MockMvc上传文件并从控制器获得答案。

答案 1 :(得分:1)

方法参数值是否捕获了您正在寻找的内容?

您可以使用模拟框架来模拟其余模板并捕获方法调用参数。

我建议使用TestNGJMockit

控制器类(示例是工作代码,所以我想显示整个控制器类):

import org.springframework.beans.factory.annotation.*;
import org.springframework.http.MediaType;
import org.springframework.util.*;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.multipart.MultipartFile;

public class MyController {

    @Autowired
    @Qualifier("MyRestTemplate")
    private RestTemplate restTemplate;

    @RequestMapping(method = RequestMethod.POST, consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    @ResponseBody
    public String formForUpload(@RequestParam("file") MultipartFile file) {
        MultiValueMap<String, Object> parts = new LinkedMultiValueMap<>();
        restTemplate.postForObject("http://localhost:8080/rest/something", parts, MultipartFile.class);
        return "[{\"good-data\"}]";
    }
}

单元测试:

import mockit.*;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.multipart.MultipartFile;
import org.testng.Assert;
import org.testng.annotations.Test;

import java.util.ArrayList;

public class MyControllerTest {

    @Test
    public void testFormForUpload(@Mocked final RestTemplate restTemplate) {
        MyController controller = new MyController();
        Deencapsulation.setField(controller, restTemplate);

        final ArrayList<String> urlValueHolder = new ArrayList<>();

        new Expectations() {{
            restTemplate.postForObject(withCapture(urlValueHolder), any, MultipartFile.class);
        }};

        controller.formForUpload(new MockMultipartFile("data-file.txt", "various data".getBytes()));

        Assert.assertEquals(urlValueHolder.size(), 1);
        Assert.assertEquals(urlValueHolder.get(0), "http://localhost:8080/rest/something");
    }
}

注意:

  • RestTemplate模拟作为测试方法的参数传递。这就是创建工作模拟所需要做的一切。
  • 请参阅我withCapture调用我作为RestTemplate#postForObject的第一个参数。控制器使用的URL将存储在列表urlValueHolder
  • 为网址列表提供了两个断言 - 它被调用一次并且包含所需的网址。

我相信测试很简单,但我仍然建议重构控制器。我建议将url创建移动到单独的工厂类,而不是使用模拟和测试控制器来验证URL。对工厂进行简单的单元测试将是测试网址所需的全部内容。

答案 2 :(得分:0)

Spring Hateoas项目中有一个用于生成控制器和控制器方法链接的类。具体来看看org.springframework.hateoas.mvc.ControllerLinkBuilder。它将能够为简单的@RequestMapping注释生成URL,但如果在同一个处理程序方法上使用多个映射,则无法确定要生成哪个。

此外,如果您的控制器处理程序方法返回String,则链接构建器将抛出错误,因此将返回类型更改为Object并且它将起作用。