组合在一起使用
  要测试以上那个controller的准备工作都已经完成了,而仅仅使用单元测试是无法对其进行测试的。
  1 @RunWith(SpringJUnit4ClassRunner.class)
  2 @ContextHierarchy({
  3    @ContextConfiguration("classpath:spring/business-config.xml"),
  4   @ContextConfiguration("classpath:spring/mvc-core-config.xml")
  5 })
  6 @WebAppConfiguration
  7 @ActiveProfiles("jdbc")
  8 public class PetControllerIT {
  9
  10    @Autowired
  11    private WebApplicationContext context;
  12
  13    @Test
  14    public void should_set_pet_types_in_model() throws Exception {
  15        MockMvc mockMvc = webAppContextSetup(context).build();
  16        RequestBuilder getNewPet = get("/owners/1/pets/new");
  17        mockMvc.perform(getNewPet)
  18            .andExpect(model().attributeExists("types"))
  19            .andExpect(request().sessionAttribute("pet", instanceOf(Pet.class)));
  20    }
  21 }在这个代码片段中一共进行了以下操作:
  在第3行与第4行,我们确保Spring的配置文件都已正确配置
  在第16行,我们确保了应用程序对表单生成的URL的某个GET访问能够正确地发送响应
  在第18行,我们测试了该model的属性中包含了types这一属性
  后,在第19行,我们测试了会话中包含了pet这一属性,并且确认了它的类型确实是我们所期望的Pet类型
  (注意,instanceOf()这一静态方法是由Hamcrest API所提供的。)
  其它一些测试方面的挑战
  上文中所描述的那个PetClinic示例是有些偏简单的。它已经使用了某个内置的Hypersonic SQL数据库,自动地创建了数据库schema,并插入了一些数据,这些操作全部是在容器启动时发生的。而在常规的应用程序中,开发者很可能会在生产环境与测试环境中使用不同的数据库。此外,在生产环境中不会对数据进行初始化操作。这里的挑战包括如何切换至另一台数据库,以达到测试的目的,如何在测试执行开始前将数据库设置为必需的状态,以及如何在执行结束后检查数据库的状态。
  与之类似的是,我们已经测试了PetController中的initCreationForm()这个单一方法,而在其创建过程中也隐含了对processCreationForm()方法的调用。为了减少对测试进行初始化的代码,选择对用例本身进行测试,而不是对每个方法进行测试的做法也不无道理。而这种方法也许意味着一个巨大的测试方法:而一旦该测试失败,要找到失败的根源或许会非常困难。另一种途径是,创建细粒度的、具有良好命名的方法,并且按顺序执行它们。不幸的是,JUnit作为一种真正的单元测试框架,并不允许使用这种方式。
  每一个与某种基础设计资源,例如数据库、邮件服务器、FTP服务器等等进行打交道的组件,都面临着相同的挑战:对该资源进行mock对于测试本身来说没有带来任何价值。比方说,用户如何在测试中检查复杂的JPA查询,这不是对数据库进行mock可以实现的功能。常见的实践方法是搭建一种专用的内存数据库。根据上下文情况的不同,也可能存在更好的实现方式。在这种情况下,所面临的挑战在于如何选择正确的实现方式,以及如何在测试中管理这些资源的生命周期。
  说到基础设施的资源,在任何现代的web应用程序中,很大一部分的依赖都来自于web service。而随着微服务这一趋势的走红,情况变得更加糟糕。如果某个应用程序依赖于其它外部web service,那么测试这个应用程序与它的依赖之间的协作成为一种不可避免的需求。当然,这些web service依赖的搭建方式很大程度上依赖于它们的自然属性,在大多数情况下它们都会以SOAP或REST的方式提供。
  此外,如果应用程序的目标平台并非Spring,而是Java EE的话,所面临的挑战也会变得不同。在Java EE中提供了上下文及依赖注入(CDI)服务,其动作方式依赖于autowiring。要测试这样的应用程序,意味着要正确地将组件组合在一起,包括类与配置文件。不仅如此,Java EE还承诺相同的应用程序可以运行在不同的适用应用服务器上。如果该应用程序对应着不同的目标平台,比方说这是一个可能会部署在不同的客户环境中的产品,那么对这种兼容性也要进行彻底的测试。
  结论
  在本文中,我为读者展示了集成测试的某些技术能够让你对你的代码更有自信,我在这里使用了Spring MVC web框架作为示例。
  我同时也简要地表示,测试中存在的某些挑战是无法由单元测试本身所解决的,而必须通过集成测试实现。
  本文中的内容只是一些基础的技术,要想深入地研究其它技术以及更多的工具,请参与由我编著的《Integration Testing from the Trenches》一书,我在本书中展现了多种工具与技术,并且表现了如何使用这些工具以更好地保证你的软件质量。