【问题标题】:Spring Framework TEST RESTful Web Service (Controller) Offline i.e. No Server, No DatabaseSpring Framework 测试 RESTful Web 服务(控制器)离线,即无服务器,无数据库
【发布时间】:2012-02-26 15:43:58
【问题描述】:

我有一个非常简单的 RESTful 控制器,它使用和生成 JSON。我需要离线测试这个控制器,即没有服务器运行,没有数据库运行。而且我因为找不到解决方案而发疯。我的初始测试用例将包括:

  • 测试 REST URI,即 GET、POST、PUT、DELETE - 我必须能够根据发送的数据断言返回的数据。
  • 断言将测试 JSON 数据

我有以下 URI:

  • /pcusers - 返回所有用户
  • /pcusers/{id} - 返回特定用户
  • /pcusers/create/{pcuser} - 将用户添加到数据库
  • /pcusers/update/{pcuser} - 更新用户
  • /pcusers/delete/{id} - 删除用户

注意:这不是典型的 MVC 应用程序。我没有意见。我有一个纯 REST 控制器,它可以输出 JSON 并使用 JSON 格式的数据。

如果有人能指导我正确的方向,将不胜感激。

只是为了清楚我的代码是什么样子的:

@Controller
@RequestMapping("/pcusers")
public class PcUserController {
    protected static Logger logger = Logger.getLogger(PcUserController.class);

    @Resource(name = "pcUserService")
    private PcUserService pcUserService;

    @RequestMapping(value = "", method = RequestMethod.GET, produces = "application/json")
    @ResponseBody
    public List<PcUser> readAll() {
        logger.debug("Delegating to service to return all PcUsers");
        return pcUserService.readAll();
    }

    @RequestMapping(value = "/{id}", method = RequestMethod.GET, consumes = "application/json", produces = "application/json")
    @ResponseBody
    public PcUser read(@PathVariable String id) {
        logger.debug("Delegating to service to return PcUser " + id);
        return pcUserService.read(id);
    }

    @RequestMapping(value = "/create/{pcUser}", method = RequestMethod.POST, consumes = "application/json", produces = "application/json")
    @ResponseBody
    public boolean create(@PathVariable PcUser pcUser) {
        logger.debug("Delegating to service to create new PcUser");
        return pcUserService.create(pcUser);
    }

    @RequestMapping(value = "/update/{pcUser}", method = RequestMethod.POST, consumes = "application/json", produces = "application/json")
    @ResponseBody
    public boolean update(@PathVariable PcUser pcUser) {
        logger.debug("Delegating to service to update existing PcUser");
        return pcUserService.update(pcUser);
    }

    @RequestMapping(value = "/delete/{id}", method = RequestMethod.POST, consumes = "application/json", produces = "application/json")
    @ResponseBody
    public boolean delete(@PathVariable String id) {
        logger.debug("Delegating to service to delete existing PcUser");
        return pcUserService.delete(id);
    }
}

更新(2012 年 2 月 5 日): 经过一番研究,我发现了一个名为 spring-test-mvc 的 Spring 框架。它看起来很有希望,我已经设法在这方面取得了良好的开端。但现在我有一个新问题。当我向“/pcusers/{id}”提交 GET 请求时,控件将传递给负责处理该映射的 read 方法。在该方法中,我有一个 pcUserService 进行读取。现在,问题是当我运行这个测试时,真实控制器中的 pcUserService 实例为 NULL;因此它最终会崩溃,因为无法在 NULL 对象上调用 read。

这是 PcUserControllerTest 代码:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:/applicationContextTest.xml")
public class PcUserControllerTest {

    @Autowired
    PcUserService pcUserService;

    @Autowired
    PcUserController pcUserController;

    PcUser pcUser;

    @Before
    public void setUp() throws Exception {
        pcUser = new PcUser("John", "Li", "Weasley", "john", "john", new DateTime());

        pcUserService.create(pcUser);
    }

    public void tearDown() throws Exception {
        pcUserService.delete(pcUser.getId());
    }

    @Test
    public void shouldGetPcUser() throws Exception {
        standaloneSetup(pcUserController)
                .build()
                .perform(get("/pcusers/" + pcUser.getId()).accept(MediaType.APPLICATION_JSON))
                .andExpect(status().isOk());
    }
}

【问题讨论】:

  • 我在这里goo.gl/yywX9 提出了一个新问题,它与spring-test-mvc 框架有关。
  • 感谢您用答案更新您的问题。看起来很有趣,因为我只是在寻找类似的东西。

标签: spring testing offline-mode


【解决方案1】:

这里有一个可以给你一些想法的建议。我假设您熟悉SpringJUnit4ClassRunner@ContextConfiguration。首先创建一个包含PcUserController 和模拟PcUserService 的测试应用程序上下文。在下面的示例PcUserControllerTest 类中,Jackson 用于转换 JSON 消息,而 Mockito 用于模拟。

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(/* Insert test application context here */)
public class PcUserControllerTest {

    MockHttpServletRequest requestMock;
    MockHttpServletResponse responseMock;
    AnnotationMethodHandlerAdapter handlerAdapter;
    ObjectMapper mapper;
    PcUser pcUser;

    @Autowired
    PcUserController pcUserController;

    @Autowired
    PcUserService pcUserServiceMock;

    @Before
    public void setUp() {
        requestMock = new MockHttpServletRequest();
        requestMock.setContentType(MediaType.APPLICATION_JSON_VALUE);
        requestMock.addHeader(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE);

        responseMock = new MockHttpServletResponse();

        handlerAdapter = new AnnotationMethodHandlerAdapter();
        HttpMessageConverter[] messageConverters = {new MappingJacksonHttpMessageConverter()};
        handlerAdapter.setMessageConverters(messageConverters);

        mapper = new ObjectMapper();
        pcUser = new PcUser(...);

        reset(pcUserServiceMock);
    }
}

现在,我们有了创建测试所需的所有代码:

@Test
public void shouldGetUser() throws Exception {
    requestMock.setMethod("GET");
    requestMock.setRequestURI("/pcusers/1");

    when(pcUserServiceMock.read(1)).thenReturn(pcUser);

    handlerAdapter.handle(requestMock, responseMock, pcUserController);

    assertThat(responseMock.getStatus(), is(HttpStatus.SC_OK));
    PcUser actualPcUser = mapper.readValue(responseMock.getContentAsString(), PcUser.class);
    assertThat(actualPcUser, is(pcUser));
}


@Test
public void shouldCreateUser() throws Exception {
    requestMock.setMethod("POST");
    requestMock.setRequestURI("/pcusers/create/1");
    String jsonPcUser = mapper.writeValueAsString(pcUser);
    requestMock.setContent(jsonPcUser.getBytes());

    handlerAdapter.handle(requestMock, responseMock, pcUserController);

    verify(pcUserServiceMock).create(pcUser);
}

【讨论】:

  • 您的解决方案看起来不错,但我相信 spring-test-mvc 框架很容易处理。我已经更新了我的原始问题以包含我迄今为止编写的测试代码。现在,我有一个新问题,在更新中已在上面进行了说明。
  • 好的,我解决了这个问题。我没有在 PcUserController 上执行 @Autowired。我已经更新了我的测试代码。
  • 我在上面测试你的代码,我得到一个异常记录在这里pastebin.com/yUtzqmW8你能解释一下吗?我不明白这个例外。我相信 AnnotationMethodHandlerAdapter 里面的东西是空的。
  • 在我的 Spring 版本 (3.1) 中,在特定行上发生 NPE 的唯一方法是自动取消装箱 Booleanboolean。您是否尝试过附加源代码并对其进行调试?在我的测试中,我从来没有到达过这一行(线程存在于前面的 return 语句中)。也许您可以通过阅读type level 映射找到线索?
  • @jsinghfoss 我写了一个blog post,关于使用 spring-mvc-test 框架(有一个 1.0.0.M1 版本可用)的基本控制器测试,它删除了大部分管道代码.
猜你喜欢
  • 1970-01-01
  • 2021-11-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-11-03
  • 1970-01-01
  • 2017-09-28
  • 2013-04-16
相关资源
最近更新 更多