单元测试与 Spring 的集成测试
Posted
技术标签:
【中文标题】单元测试与 Spring 的集成测试【英文标题】:Unit tests vs integration tests with Spring 【发布时间】:2010-09-21 20:36:14 【问题描述】:我正在开发一个 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。
所以,这是我的问题:
这是我的第一个 Spring 项目 - 像我所做的那样为单个 bean 创建单元测试是否正常,然后创建第二套测试(集成测试)来测试一切是否按预期工作实际的应用程序上下文?对此是否有既定的最佳实践?
此外,如何将单元测试与集成测试分开?我有src
中的所有源代码,test
中的单元测试 - 是否应该有第二个测试文件夹(例如 test-integration
)用于集成测试用例?
由于这是我的第一个 Spring 项目,我很好奇其他人通常是如何做这种事情的 - 与其重新发明***,我更愿意询问社区的其他人。
【问题讨论】:
【参考方案1】:我不能说是最佳实践,但这是我过去所做的。
单元测试:
为重要的 bean(即大部分与 Spring 相关的 bean)创建单元测试 在可行的情况下(即,大多数情况下,如果不是所有时候),将 Mocks 用于注入服务。 在项目test
目录中为这些测试使用标准命名约定。使用Test
或TestCase
作为类名的前缀或后缀似乎被广泛使用。
集成测试:
创建一个AbstractIntegrationTestCase
来设置一个 Spring
WebApplicationContext
用于集成测试类。
使用test
目录中的集成测试的命名约定。我使用IntTest
或IntegrationTest
作为这些测试的前缀或后缀。
设置三个 Ant test
目标:
-
test-all(或任何您想命名的名称):运行单元和集成测试
test:运行单元测试(只是因为
test
似乎是单元测试最常见的用法
test-integration:运行集成测试。
如上所述,您可以使用对您的项目有意义的命名约定。
至于将单元与集成测试分离到一个单独的目录中,我认为这并不重要,只要开发人员及其工具可以轻松找到并执行它们。
例如,我使用 Spring 处理的最后一个 Java 项目使用的正是上述内容,集成测试和单元测试位于同一 test
目录中。另一方面,Grails 项目在通用测试目录下明确分离单元和集成测试目录。
【讨论】:
这听起来是个好策略。但是由于单元测试和集成测试在同一个目录中,Eclipse 无法区分它们,是吗? Eclipse 运行 junit 测试的唯一选择是只运行一个或在某个文件夹中运行所有测试 - 不能像 Ant 那样按名称拆分它们。 我不得不回去检查 Eclipse - 你是对的,如果它们在同一个目录中,我看不到区分测试的方法。我的上一个 Spring 项目是在 Idea/Intellij 中完成的,时间已经够长了,以至于我不记得确切的 IDE 配置是什么了。【参考方案2】:几个孤立的点:
是的,这是 Spring 测试的常用方法 - 单独的单元测试和集成测试,前者不加载任何 Spring 上下文。
对于您的单元测试,也许可以考虑模拟以确保您的测试集中在一个独立的模块上。
如果您的测试包含大量依赖项,那么它们就不是真正的单元测试。它们是集成测试,您使用新而不是依赖注入来连接依赖关系。当您的生产应用程序使用 Spring 时浪费时间和重复工作!
用于调出 Spring 上下文的基本集成测试很有用。
@required 注释可以帮助您确保在 Spring 接线中捕获所需的依赖项。
也许看看 Maven,它会给你明确的阶段来绑定你的单元和集成测试。 Maven 在 Spring 社区中被广泛使用。
【讨论】:
【参考方案3】:如果你也切换到纯注解机制,使用 @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 线接线并不少见。组件测试缓解了这个问题。
【讨论】:
不幸的是我还在使用 Java 1.4 :(【参考方案4】:使用 InitializingBean 接口(实现方法“afterPropertiesSet”)或为您的 bean 指定一个 init 方法。 InitializingBean 通常更容易,因为您不需要记住将 init 方法添加到您的 bean。
使用 afterPropertiesSet 确保所有内容都作为非 null 注入,如果为 null,则抛出异常。
【讨论】:
【参考方案5】:当我为 Web 应用程序创建集成测试时,我将它们放在一个单独的目录中。它们是使用 jUnit 或 TestNG 构建的,并使用 Selenium 之类的东西与被测系统交互,就像他们是用户一样点击网页。循环如下:编译、运行单元测试、构建 Web 应用程序、将其部署到正在运行的服务器、执行测试、取消部署应用程序并报告结果。这个想法是测试整个系统。
【讨论】:
【参考方案6】:关于将单元测试与集成测试分开运行,我将所有后者放入一个集成测试目录中,并使用 IDE/Ant 使用this 之类的方法运行它们。对我有用。
【讨论】:
【参考方案7】:单元测试和集成测试之间的区别在于,单元测试不一定加载您的上下文,您专注于您编写的代码 - 它通过模拟任何依赖调用快速失败,即有无异常在里面。 但在集成测试的情况下,您需要加载上下文并像实际场景一样执行端到端测试。
【讨论】:
以上是关于单元测试与 Spring 的集成测试的主要内容,如果未能解决你的问题,请参考以下文章
单元测试在测试spring集成TCP组件时创建名为“amqAdmin”的bean时出错