使用Spring设计和实现Web应用程序 开发指引(三)

翻译自 http://spring.io/guides/tutorials/web/2/

首发于 http://my.oschina.net/u/179755/blog/233176


2步:实现URL和返回数据

我们已经决定了我们的WebURL,将它们作为Web域组件捕获到我们的救生圈中

195718_fqPB_179755.png

现在是时候来实现我们的Yummy面馆Web前端了。用Spring MVC构建服务的第一步是构建和测试一个或多个控制器,这些控制器负责处理我们在前一步骤中的定义的HTTP请求。

 

从一个失败的测试开始

测试驱动开发告诉我们,如果我们没有失败的测试,那么我们无需写任何代码。所以在我们深入实现服务前,我们需要创建一些测试,这些测试将驱使鼓励我们去写代码以通过这些测试。

将查询从命令中分离出来

在我们开始创建测试之前,我们需要考虑我们的服务将要响应的请求的分类。我们将编写测试,查找所有我们在第1步中所设计的HTTP交互。

这些交互可以分为3类:

  1. 读取或者查询菜单的请求

  2. 更新篮的请求

  3. 创建订单的请求

我们进一步将这些分成2类:

  1. 改变资源状态的请求(命令)

  2. 查询资源状态的请求(查询)

我们可以为每个资源创建一个控制器来实现这两类交互。但是命令-查询-责任分离模式建议我们通过应用程序来拆分这些责任到不同的路由。在本教程中,我们将分别实现这些。

在正确的层面测试

编写代码时,我们可以选择编写什么测试,和多少来隔离我们正在测试的代码。我们应用的隔离度定义了我们正在写的测试的类型。这里有三个主要的层面,单元,集成和功能。

正如Mike CohnMartin Fowler所说,这些组成了测试金字塔。

我们应该写尽可能多的单元测试,作为在金字塔的底层,少一点的集成测试,更少的功能测试。

Spring MockMvc测试

Spring提供了编写金字塔各层测试的全面支持。

我们需要写一个Spring MVC控制器,它包含了众多注解来定义它自己的行为。这些行为需要测试,我们才能确定它的正确性。

Spring提供了MockMVC以作为测试方案,它允许我们编写如Martin Fowler所说的皮下测试,获得跟在一个完整的Web容器中一样的执行效果。

定义行为,创建测试

我们第一个实现的URL是“/”。它是Yummy面馆网站的根,后面称为网站URL。它将包含有效的菜单项列表,允许用户增加菜单项到篮中,提供了一个机制来存放订单。

第一件事情是让这个网站URL有效。

第一步,我们需要创建一个全新的空类,com.yummynoodlebar.web.controller.SiteController,来实现这个控制器。

在我们实现这个控制器之前,我们创建一个测试com.yummynoodlebar.web.controller.SiteIntegrationTest

package   com.yummynoodlebar.web.controller;

 

import   com.yummynoodlebar.core.services.MenuService;

import   com.yummynoodlebar.events.menu.RequestAllMenuItemsEvent;

import   com.yummynoodlebar.web.controller.fixture.WebDataFixture;

import   org.junit.Before;

import   org.junit.Test;

import   org.mockito.InjectMocks;

import   org.mockito.Mock;

import   org.mockito.MockitoAnnotations;

import   org.springframework.test.web.servlet.MockMvc;

 

import   static org.mockito.Matchers.any;

import   static org.mockito.Mockito.when;

import   static   org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;

import   static   org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;

import   static   org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;

import   static   org.springframework.test.web.servlet.setup.MockMvcBuilders.standaloneSetup;

 

public   class SiteIntegrationTest {

 

    private static final String RESPONSE_BODY   = "Yummy Noodles,Special Yummy Noodles,Low cal Yummy Noodles";

 

    MockMvc mockMvc;

 

    @InjectMocks

    SiteController controller;

 

    @Mock

    MenuService menuService;

 

    @Before

    public void setup() {

        MockitoAnnotations.initMocks(this);

 

        mockMvc =   standaloneSetup(controller).build();

 

          when(menuService.requestAllMenuItems(any(RequestAllMenuItemsEvent.class))).thenReturn(WebDataFixture.allMenuItems());

 

    }

 

    @Test

    public void thatTextReturned() throws   Exception {

        mockMvc.perform(get("/"))

        .andDo(print())

          .andExpect(content().string(RESPONSE_BODY));

 

    }

 

}

 

 

执行该单个测试

$   ./gradlew -Dtest.single=SiteIntegrationTest test

这个测试毫无疑问是失败的。

:compileJava

:processResources   UP-TO-DATE

:classes

:compileTestJava

:processTestResources   UP-TO-DATE

:testClasses

:test

 

com.yummynoodlebar.web.controller.SiteIntegrationTest   > thatTextReturned STARTED

 

com.yummynoodlebar.web.controller.SiteIntegrationTest   > thatTextReturned FAILED

    java.lang.AssertionError at   SiteIntegrationTest.java:44

 

1 test   completed, 1 failed

:test   FAILED

 

FAILURE:   Build failed with an exception.

 

因为我们并没有实现和配置这个控制器。现在我们可以安全地实现这个控制器。


创建控制器和映射

我们正在创建一个交互的HTML网站给用户使用,但是我们还需要有一个基本的控制器返回文本给用户。

如前所述,我们需要构建控制器查询菜单项。现在,我们期望这个测试让菜单项显示在一个明文文件中,以逗号分隔。

src/main/java/com/yummynoodlebar/web/controller/SiteController.java

package   com.yummynoodlebar.web.controller;

 

import   org.slf4j.Logger;

import   org.slf4j.LoggerFactory;

import   org.springframework.beans.factory.annotation.Autowired;

import   org.springframework.stereotype.Controller;

import   org.springframework.web.bind.annotation.RequestMapping;

import   org.springframework.web.bind.annotation.RequestMethod;

import   org.springframework.web.bind.annotation.ResponseBody;

 

import   com.yummynoodlebar.core.services.MenuService;

import   com.yummynoodlebar.events.menu.AllMenuItemsEvent;

import   com.yummynoodlebar.events.menu.MenuItemDetails;

import   com.yummynoodlebar.events.menu.RequestAllMenuItemsEvent;

 

@Controller

@RequestMapping("/")

public   class SiteController {

 

    private static final Logger LOG =   LoggerFactory.getLogger(SiteController.class);

 

    @Autowired

    private MenuService menuService;

 

    @RequestMapping(method = RequestMethod.GET)

    @ResponseBody

    public String getCurrentMenu() {

        LOG.debug("Yummy Menu directly   to ResponseBody");

        return   prettyPrint(menuService.requestAllMenuItems(new RequestAllMenuItemsEvent()));

    }

 

    private String   prettyPrint(AllMenuItemsEvent requestAllMenuItems) {

        StringBuffer sb = new StringBuffer();

        String delim = "";

        for (MenuItemDetails menuItemDetails   : requestAllMenuItems.getMenuItemDetails()) {

            sb.append(delim).append(menuItemDetails.getName());

            delim = ",";

        }

 

        return sb.toString();

    }

 

}

 

再次执行测试,将通过。

:compileJava

:processResources

:classes

:compileTestJava

:processTestResources   UP-TO-DATE

:testClasses

:test

 

com.yummynoodlebar.web.controller.SiteIntegrationTest   > thatTextReturned STARTED

 

com.yummynoodlebar.web.controller.SiteIntegrationTest   > thatTextReturned PASSED

 

BUILD   SUCCESSFUL

 

这个控制器目前从服务返回菜单并把它转换成了文本。


总结

祝贺。我们已经创建了一个控制器,它实现了我们网站的一部分。我们已经使用MockMVC,而不是在容器中,来测试了这个控制器,确保这个处理映射是正确的。

我们现在的救生圈图现在在Web域包含了新组件,SiteController

195739_sGje_179755.png

整体的救生圈图将看起来如下:

195758_m0nV_179755.png

 

下一步:部署服务。

 


转载于:https://my.oschina.net/u/179755/blog/233176

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值