39

我正在开发一个 Spring MVC 项目,并且我对源代码树中的所有各种组件进行了单元测试。

例如,如果我有一个HomeController需要LoginService注入的控制器,那么在我的单元测试中,HomeControllerTest我只需像平常一样(在 Spring 之外)实例化对象并注入属性:

protected void setUp() throws Exception {
    super.setUp();
    //...
    controller = new HomeController();
    controller.setLoginService( new SimpleLoginService() );
    //...
}

这对于将每个组件作为一个独立的单元进行测试非常有用——除了现在我在项目中有几十个类,在编写了一个类并为它编写了一个成功的单元测试之后,我一直忘记更新我的 Spring MVC 上下文文件已部署应用程序中的实际接线。我发现当我将项目部署到 Tomcat 时忘记更新上下文文件并从非连线 bean 中找到一堆 NullPointers。

所以,这是我的问题:

  1. 这是我的第一个 Spring 项目 - 像我所做的那样为单个 bean 创建单元测试是否正常,然后创建第二套测试(集成测试)来测试所有内容在实际应用程序上下文中是否按预期工作?对此是否有既定的最佳实践?

  2. 此外,如何将单元测试与集成测试分开?我有所有源代码src,单元测试在test- 是否应该有第二个测试文件夹(例如test-integration)用于集成测试用例?

由于这是我的第一个 Spring 项目,我很好奇其他人通常是如何做这种事情的——与其重新发明轮子,我更愿意询问社区的其他人。

4

7 回答 7

32

我不能说是最佳实践,但这是我过去所做的。

单元测试:

  • 为重要的 bean 创建单元测试(即,大多数与 Spring 相关的 bean)
  • 在可行的情况下使用 Mocks 注入服务(即,如果不是所有时间的话,大多数情况下)。
  • test在项目目录中为这些测试使用标准命名约定。使用TestorTestCase作为类名的前缀或后缀似乎被广泛使用。

集成测试:

  • 创建一个用于集成测试类的AbstractIntegrationTestCase设置。Spring WebApplicationContext
  • test对目录中的集成测试使用命名约定。我使用IntTestorIntegrationTest作为这些测试的前缀或后缀。

设置三个 Anttest目标:

  1. test-all(或任何你想命名的):运行单元和集成测试
  2. 测试:运行单元测试(只是因为test似乎是单元测试最常见的用法
  3. test-integration:运行集成测试。

如前所述,您可以使用对您的项目有意义的命名约定。

至于将单元与集成测试分离到一个单独的目录中,我认为只要开发人员及其工具可以轻松找到并执行它们,这并不重要。

例如,我使用 Spring 处理的最后一个 Java 项目完全使用了上面描述的内容,集成测试和单元测试位于同一test目录中。另一方面,Grails 项目将单元测试目录和集成测试目录明确地分开在一个通用测试目录下。

于 2008-11-11T19:03:23.767 回答
6

几个孤立的点:

是的,这是 Spring 测试的一种常用方法——单独的单元测试和集成测试,前者不加载任何 Spring 上下文。

对于您的单元测试,也许考虑模拟以确保您的测试集中在一个独立的模块上。

如果您的测试包含大量依赖项,那么它们并不是真正的单元测试。它们是集成测试,您使用新而不是依赖注入来连接依赖关系。当您的生产应用程序使用 Spring 时,浪费时间和重复工作!

用于调出 Spring 上下文的基本集成测试很有用。

@required 注释可以帮助您确保在 Spring 接线中捕获所需的依赖项。

也许看看 Maven,它会给你明确的阶段来绑定你的单元和集成测试。Maven 在 Spring 社区中被广泛使用。

于 2010-08-15T07:53:37.937 回答
4

如果你也切换到纯注释机制,使用 @Component、@Controller、@Service 和 @Repository 注释所有 bean,那么 Spring 的许多繁琐的双重簿记就会消失。只需将 @Autowired 添加到您需要注入的属性中。

请参阅弹簧参考手册的第 3.11 节。http://static.springframework.org/spring/docs/2.5.x/reference/beans.html#beans-annotation-config

在相关的说明中,我们一直在使用 KenG 描述的划分单元/集成测试。在我最近的方案中,我们还引入了第三个“类”测试,“ComponentTests”。它们使用完整的 spring 布线运行,但使用有线存根实现(在 spring 中使用组件扫描过滤器和注释)。

我们这样做的原因是因为对于某些“服务”层,您最终会使用大量的手工编码连接逻辑来手动连接 bean,有时还会使用大量可笑的模拟对象。5 线测试 100 线接线并不少见。组件测试缓解了这个问题。

于 2008-11-11T19:18:30.273 回答
2

使用 InitializingBean 接口(实现方法“afterPropertiesSet”)或为您的 bean 指定一个 init 方法。InitializingBean 通常更容易,因为您不需要记住将 init 方法添加到 bean。

使用 afterPropertiesSet 确保所有内容都作为非 null 注入,如果为 null,则抛出异常。

于 2008-11-11T20:28:38.820 回答
0

当我为 Web 应用程序创建集成测试时,我将它们放在一个单独的目录中。它们是使用 jUnit 或 TestNG 构建的,并使用Selenium之类的东西与被测系统交互,就像他们是用户一样点击网页。循环如下:编译、运行单元测试、构建 Web 应用程序、将其部署到正在运行的服务器、执行测试、取消部署应用程序并报告结果。这个想法是测试整个系统。

于 2008-11-11T19:23:46.377 回答
0

关于将单元测试与集成测试分开运行,我将所有后者放入一个集成测试目录中,并使用 IDE/Ant 使用类似这样的方法运行它们。为我工作。

于 2009-09-16T12:51:38.727 回答
0

单元测试和集成测试之间的区别是,单元测试不一定加载您的上下文,您专注于您编写的代码 - 它通过模拟任何依赖调用来快速失败,即有无异常。但是在集成测试的情况下,您加载上下文并像实际场景一样执行端到端测试。

于 2013-03-17T18:09:57.690 回答