注意这里并没有对返回视图这一结果进行测试,因为对于它的测试与controller中的代码将完全相同。
  在单元测试中缺少了什么
  到目前火上,我们已经成功地实现了对该方法的代码覆盖率,我们也可以选择告一段落了。但是,如果在代码完成单元测试之后停止测试,其结果像是在生产汽车时,在测试过车辆的每一个螺母和螺栓之后直接开始组装汽车一样。很显然,没有人愿意承担如此巨大的风险。在实际生活中,首先要对汽车进行全面的测试驱动,以检查所有参与整体汽车动作的部件,而不仅仅是螺母与螺栓的组装。而在软件开发世界中,我们把这种类似的测试驱动称为集成测试。集成测试是目的是确保各种类之间能够正确地进行协作。
  在Java世界中,Spring框架与Java EE平台都可以被视为一种容器,它们为各种可用的服务提供了API,例如使用JDBC进行数据库访问。要确保使用Spring或Java EE进行开发的应用程序能够正常地运行,需要在容器中进行测试,以测试它们与容器中所提供的服务的集成是否能够正常运行。
  在以上所举的示例中,还有一部分内容没有测试到,也无法进行测试:
  Spring的配置,即通过autowiring方式将所有类进行组装
  在model中对PetType的加载,即populatePetTypes()方法
  URL是否映射到正确的controller,以及方法标注,即@RequestMapping
  在HTTP会话中对Pet实例的设置,即@SessionAttributes("pet")标注
  容器内测试
  集成测试,以及特定的“容器内测试”正是测试以上提过的这些测试点的解决方案。幸运的是,Spring中提供了一套完整的测试框架,旨在实现这一目的。而Java EE的用户也可以通过使用Arquillian测试框架加入容器内测试的过程中。不过在Java EE的应用程序中存在着不同的类装配方法,例如CDI,而Arquillian中也提供了处理这些差异的方法。有了这些背景知识之后,让我们重新回来看一看这个Pet Clinic示例,并且为上面提过的这些测试点创建测试方法。
  Spring中的JUnit集成
  正如JUnit的名称所暗示的一样,这是一种用于单元测试的框架。Spring中提供了一种专用的JUnit执行器,它能够在测试开始运行时启动Spring容器。这是在测试类中通过@RunWith标注进行配置的。
  @RunWith(SpringJUnit4ClassRunner.class)
  public class PetControllerIT { ... }Spring框架也有着自己的配置组件集,可以使用老式的XML文件配置,或选择近流行的Java“配置”类。这里有一种的实践,是使用细粒度的配置组件,因此使用者可以自由选择所需的组件,并按照测试的上下文进行组合。这些配置组件可以通过测试类的@ContextConfiguration标注进行设置。
  @ContextConfiguration("classpath:spring/business-config.xml")
  public class PetControllerIT { ... }后,Spring框架允许根据某种跨整个应用程序范围的标记,又称为档案,对某些配置选项进行激活(或是关闭)。使用档案的方式相当简单,是在在测试类中设置一个@ActiveProfile标注即可。
  @ActiveProfiles("jdbc")
  public class PetControllerIT { ... }要使用JUnit测试标准的Spring bean的话,以上的方式已经足够了。但要测试Spring MVC controller的话,你还需要进行更多的工作。
  Spring中的测试web上下文
  对于web应用程序来说,Spring能够创建一个结构化的上下文,类似于分层架构中的父-子关系。子结点中的内容与web相关,例如controller、格式化器、资源包等等。而父结点中包含了其它部分的内容,例如服务与仓储(repository)等等。为了模仿这种关系,需要在测试类中使用@ContextHierarchy标注,并且对该标注进行配置,让它引用必需的@ContextConfiguration标注:
  在下面这个测试片段中,business-config.xml代表了父,而mvc-core-config.xml则代表了子:
  @ContextHierarchy({
  @ContextConfiguration("classpath:spring/business-config.xml"),
  @ContextConfiguration("classpath:spring/mvc-core-config.xml")
  })使用者还需要设置@WebAppConfiguration标注,以模仿一个WebApplicationContext的行为,而不是使用更简单的ApplicationContext。
  测试controller
  在测试中按照以上描述的方法设置好web上下文之后,终于能够开始测试controller了。MockMvc类是实现这一切的入口点,这个类中包含了以下属性:
  一个Request生成器,以创建一个Fake的请求
  一个Request匹配器,以检查controller的方法执行的结果
  一个Result处理器,可以对结果进行任意地操作
  MockMvc的实例是通过调用MockMvcBuilders类的静态方法所生成的,其中某个方法生成的实例用于特定的controller集,而另一个方法生成的实例用于整个应用程序的上下文。在后一种情况下,需要提供一个WebApplicationContext的实例以作为参数。实现这一点非常简单,只需在测试类中对该类型的某个属性使用autowire即可。
  @RunWith(SpringJUnit4ClassRunner.class)
  public class PetControllerIT {
  @Autowired
  private WebApplicationContext context;
  }接下来,通过perform(RequestBuilder)方法执行经过配置的MockMvc实例方法,而RequestBuilder的实例又是通过调用MockMvcRequestBuilders中的静态方法所生成的。其中每一个静态方法都是一种执行特定HTTP方法的途径。
  总结一下,使用者可以通过以下代码模仿一个对/owners/1/pets/new路径的GET调用。
  MockMvc mockMvc = MockMvcBuilders.standaloneSetup(PetController.class).build();
  RequestBuilder getNewPet = MockMvcRequestBuilders.get("/owners/1/pets/new");
  mockMvc.perform(getNewPet);后,Spring还通过ResultMatcher接口提供了大量的断言功能,并且提供了MockMvc的fluent API:包括cookie、内容体、底层模型、HTTP状态码,所有这些内容都可以进行检查或实现更多操作。