Junit使用教程(四)

一、会用Spring测试套件的好处

在开发基于Spring的应用时,如果你还直接使用Junit进行单元测试,那你就错过了Spring为我们所提供的饕餮大餐了。使用Junit直接进行单元测试有以下四大不足:

1)导致多次Spring容器初始化问题

根据JUnit测试方法的调用流程,每执行一个测试方法都会创建一个测试用例的实例并调用setUp()方法。由于一般情况下,我们在setUp()方法中初始化Spring容器,这意味着如果测试用例有多少个测试方法,Spring容器就会被重复初始化多次。虽然初始化Spring容器的速度并不会太慢,但由于可能会在Spring容器初始化时执行加载Hibernate映射文件等耗时的操作,如果每执行一个测试方法都必须重复初始化Spring容器,则对测试性能的影响是不容忽视的;

使用Spring测试套件,Spring容器只会初始化一次

2)需要使用硬编码方式手工获取Bean

在测试用例类中我们需要通过ctx.getBean()方法从Spirng容器中获取需要测试的目标Bean,并且还要进行强制类型转换的造型操作。这种乏味的操作迷漫在测试用例的代码中,让人觉得烦琐不堪;

使用Spring测试套件,测试用例类中的属性会被自动填充Spring容器的对应Bean,无须在手工设置Bean!

3)数据库现场容易遭受破坏

测试方法对数据库的更改操作会持久化到数据库中。虽然是针对开发数据库进行操作,但如果数据操作的影响是持久的,可能会影响到后面的测试行为。举个例子,用户在测试方法中插入一条ID为1的User记录,第一次运行不会有问题,第二次运行时,就会因为主键冲突而导致测试用例失败。所以应该既能够完成功能逻辑检查,又能够在测试完成后恢复现场,不会留下“后遗症”;

使用Spring测试套件,Spring会在你验证后,自动回滚对数据库的操作,保证数据库的现场不被破坏,因此重复测试不会发生问题!

4)不方便对数据操作正确性进行检查

假如我们向登录日志表插入了一条成功登录日志,可是我们却没有对t_login_log表中是否确实添加了一条记录进行检查。一般情况下,我们可能是打开数据库,肉眼观察是否插入了相应的记录,但这严重违背了自动测试的原则。试想在测试包括成千上万个数据操作行为的程序时,如何用肉眼进行检查?

只要你继承Spring的测试套件的用例类,你就可以通过jdbcTemplate(或Dao等)在同一事务中访问数据库,查询数据的变化,验证操作的正确性!

Spring提供了一套扩展于Junit测试用例的测试套件,使用这套测试套件完全解决了以上四个问题,让我们测试Spring的应用更加方便。这个测试套件主要由org.springframework.test包下的若干类组成,使用简单快捷,方便上手。

二、使用方法

1)基本用法

[java] view plaincopy

  1. package com.test;
  2. import javax.annotation.Resource;
  3. import org.junit.Test;
  4. import org.junit.runner.RunWith;
  5. import org.springframework.test.context.ContextConfiguration;
  6. import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
  7. @RunWith(SpringJUnit4ClassRunner.class)
  8. @ContextConfiguration(locations = { "classpath:config/applicationContext-*.xml", "classpath:services/ext/service-*.xml" })
  9. public class UserServiceTest {
  10. @Resource
  11. private IUserService userService;
  12. @Test
  13. public void testAddOpinion1() {
  14. userService.downloadCount(1);
  15. System.out.println(1);
  16. }
  17. @Test
  18. public void testAddOpinion2() {
  19. userService.downloadCount(2);
  20. System.out.println(2);
  21. }
  22. }

@RunWith(SpringJUnit4ClassRunner.class) 用于配置spring中测试的环境

@ContextConfiguration(locations = { "classpath:config/applicationContext-*.xml", "classpath:services/ext/service-*.xml" })用于指定配置文件所在的位置

@Resource注入Spring容器Bean对象,注意与@Autowired区别

2)事务用法

[java] view plaincopy

  1. package com.test;
  2. import javax.annotation.Resource;
  3. import org.junit.Test;
  4. import org.junit.runner.RunWith;
  5. import org.springframework.test.annotation.Rollback;
  6. import org.springframework.test.context.ContextConfiguration;
  7. import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
  8. import org.springframework.test.context.transaction.TransactionConfiguration;
  9. import org.springframework.transaction.annotation.Transactional;
  10. @RunWith(SpringJUnit4ClassRunner.class)
  11. @ContextConfiguration(locations = { "classpath:config/applicationContext-*.xml", "classpath:services/ext/service-*.xml" })
  12. @Transactional
  13. @TransactionConfiguration(transactionManager = "transactionManager")
  14. //@TransactionConfiguration(transactionManager = "transactionManager", defaultRollback = true)
  15. public class UserServiceTest {
  16. @Resource
  17. private IUserService userService;
  18. @Test
  19. //  @Transactional
  20. public void testAddOpinion1() {
  21. userService.downloadCount(1);
  22. System.out.println(1);
  23. }
  24. @Test
  25. @Rollback(false)
  26. public void testAddOpinion2() {
  27. userService.downloadCount(2);
  28. System.out.println(2);
  29. }
  30. }

@TransactionConfiguration(transactionManager="transactionManager")读取Spring配置文件中名为transactionManager的事务配置,defaultRollback为事务回滚默认设置。该注解是可选的,可使用@Transactional与@Rollback配合完成事务管理。当然也可以使用@Transactional与@TransactionConfiguration配合。

@Transactional开启事务。可放到类或方法上,类上作用于所有方法。

@Rollback事务回滚配置。只能放到方法上。

3)继承AbstractTransactionalJUnit4SpringContextTests

[java] view plaincopy

  1. package com.test;
  2. import javax.annotation.Resource;
  3. import org.junit.Test;
  4. import org.springframework.test.context.ContextConfiguration;
  5. import org.springframework.test.context.junit4.AbstractTransactionalJUnit4SpringContextTests;
  6. import org.springframework.test.context.transaction.TransactionConfiguration;
  7. @ContextConfiguration(locations = { "classpath:config/applicationContext-*.xml", "classpath:services/ext/service-*.xml" })
  8. @TransactionConfiguration(transactionManager = "transactionManager", defaultRollback = false)
  9. public class UserServiceTest extends AbstractTransactionalJUnit4SpringContextTests {
  10. @Resource
  11. private IUserService userService;
  12. @Test
  13. public void testAddOpinion1() {
  14. userService.downloadCount(1);
  15. System.out.println(1);
  16. }
  17. @Test
  18. public void testAddOpinion2() {
  19. userService.downloadCount(2);
  20. System.out.println(2);
  21. }
  22. }

AbstractTransactionalJUnit4SpringContextTests:这个类为我们解决了在web.xml中配置OpenSessionInview所解决的session生命周期延长的问题,所以要继承这个类。该类已经在类级别预先配置了好了事物支持,因此不必再配置@Transactional和@RunWith

4)继承

[java] view plaincopy

  1. package com.test;
  2. import org.springframework.test.context.ContextConfiguration;
  3. import org.springframework.test.context.junit4.AbstractTransactionalJUnit4SpringContextTests;
  4. import org.springframework.test.context.transaction.TransactionConfiguration;
  5. @ContextConfiguration(locations = { "classpath:config/applicationContext-*.xml", "classpath:services/ext/service-*.xml" })
  6. @TransactionConfiguration(transactionManager = "transactionManager")
  7. public class BaseTestCase extends AbstractTransactionalJUnit4SpringContextTests {
  8. }

[java] view plaincopy

  1. package com.test;
  2. import javax.annotation.Resource;
  3. import org.junit.Test;
  4. import org.springframework.test.annotation.Rollback;
  5. public class UserServiceTest extends BaseTestCase {
  6. @Resource
  7. private IUserService userService;
  8. @Test
  9. public void testAddOpinion1() {
  10. userService.downloadCount(1);
  11. System.out.println(1);
  12. }
  13. @Test
  14. @Rollback(false)
  15. public void testAddOpinion2() {
  16. userService.downloadCount(2);
  17. System.out.println(2);
  18. }
  19. }

5)综合

[java] view plaincopy

  1. @RunWith(SpringJUnit4ClassRunner.class)
  2. @ContextConfiguration
  3. @TransactionConfiguration
  4. @Transactional
  5. public class PersonDaoTransactionUnitTest extends AbstractTransactionalJUnit4SpringContextTests {
  6. final Logger logger = LoggerFactory.getLogger(PersonDaoTransactionUnitTest.class);
  7. protected static int SIZE = 2;
  8. protected static Integer ID = new Integer(1);
  9. protected static String FIRST_NAME = "Joe";
  10. protected static String LAST_NAME = "Smith";
  11. protected static String CHANGED_LAST_NAME = "Jackson";
  12. @Autowired
  13. protected PersonDao personDao = null;
  14. /**
  15. * Tests that the size and first record match what is expected before the transaction.
  16. */
  17. @BeforeTransaction
  18. public void beforeTransaction() {
  19. testPerson(true, LAST_NAME);
  20. }
  21. /**
  22. * Tests person table and changes the first records last name.
  23. */
  24. @Test
  25. public void testHibernateTemplate() throws SQLException {
  26. assertNotNull("Person DAO is null.", personDao);
  27. Collection<Person> lPersons = personDao.findPersons();
  28. assertNotNull("Person list is null.", lPersons);
  29. assertEquals("Number of persons should be " + SIZE + ".", SIZE, lPersons.size());
  30. for (Person person : lPersons) {
  31. assertNotNull("Person is null.", person);
  32. if (ID.equals(person.getId())) {
  33. assertEquals("Person first name should be " + FIRST_NAME + ".", FIRST_NAME, person.getFirstName());
  34. assertEquals("Person last name should be " + LAST_NAME + ".", LAST_NAME, person.getLastName());
  35. person.setLastName(CHANGED_LAST_NAME);
  36. personDao.save(person);
  37. }
  38. }
  39. }
  40. /**
  41. * Tests that the size and first record match what is expected after the transaction.
  42. */
  43. @AfterTransaction
  44. public void afterTransaction() {
  45. testPerson(false, LAST_NAME);
  46. }
  47. /**
  48. * Tests person table.
  49. */
  50. protected void testPerson(boolean beforeTransaction, String matchLastName) {
  51. List<Map<String, Object>> lPersonMaps = simpleJdbcTemplate.queryForList("SELECT * FROM PERSON");
  52. assertNotNull("Person list is null.", lPersonMaps);
  53. assertEquals("Number of persons should be " + SIZE + ".", SIZE, lPersonMaps.size());
  54. Map<String, Object> hPerson = lPersonMaps.get(0);
  55. logger.debug((beforeTransaction ? "Before" : "After") + " transaction.  " + hPerson.toString());
  56. Integer id = (Integer) hPerson.get("ID");
  57. String firstName = (String) hPerson.get("FIRST_NAME");
  58. String lastName = (String) hPerson.get("LAST_NAME");
  59. if (ID.equals(id)) {
  60. assertEquals("Person first name should be " + FIRST_NAME + ".", FIRST_NAME, firstName);
  61. assertEquals("Person last name should be " + matchLastName + ".", matchLastName, lastName);
  62. }
  63. }
  64. }

@BeforeTransaction在事务之前执行

@AfterTransaction在事务之后执行

@NotTransactional不开启事务

好了,本篇作为Junit补充就说到这里了,希望大家多多分享经验哦。

时间: 2024-10-19 16:01:33

Junit使用教程(四)的相关文章

Junit 4 Tutorials(Junit 4 教程) 四、Junit4 参数化测试

Junit 4 参数化测试 允许通过变化范围的参数值来测试方法.参数擦测试可以通过以下简单的步骤实现: 对测试类添加注解 @RunWith(Parameterized.class) 将需要使用变化范围参数值测试的参数定义为私有变量 使用上一步骤声明的私有变量作为入参,创建构造函数 .创建一个使用@Parameters注解的公共静态方法,它将需要测试的各种变量值通过集合的形式返回. 使用定义的私有变量定义测试方法 Junit 4 参数化测试样例 EvenNumberChecker.java 校验输

Junit使用教程(三)

四.实例总结 1. 参数化测试 有时一个测试方法,不同的参数值会产生不同的结果,那么我们为了测试全面,会把多个参数值都写出来并一一断言测试,这样有时难免费时费力,这是我们便可以采用参数化测试来解决这个问题.参数化测试就好比把一个“输入值,期望值”的集合传入给测试方法,达到一次性测试的目的. [java] view plaincopy package test; import static org.junit.Assert.*; import java.util.Arrays; import or

史上最详细的Android Studio系列教程四--Gradle基础

史上最详细的Android Studio系列教程四--Gradle基础

微信开放平台 公众号第三方平台开发 教程四 代公众号调用接口的SDK和demo

原文:微信开放平台 公众号第三方平台开发 教程四 代公众号调用接口的SDK和demo 教程导航: 微信开放平台 公众号第三方平台开发 教程一 平台介绍 微信开放平台 公众号第三方平台开发 教程二 创建公众号第三方平台 微信开放平台 公众号第三方平台开发 教程三 一键登录授权给第三方平台 微信开放平台 公众号第三方平台开发 教程四 代公众号调用接口的SDK和demo 前几章中我讲解了微信开发平台提供第三方平台的好处,和使用流程,如果你看了我的文章相信你对开放平台有了初步的了解,但是在实际的开发过程

Junit 4 Tutorials(Junit 4 教程)

本教程是比较全面的.较新的Junit 4 实用教程,译自:http://www.javatutorials.co.in/junit-4/,希望对大家有所帮助! 以下是本教程的内容设置,读者可以点击以下标题进行阅读: Junit测试框架介绍 Junit 测试框架是最流行的Java单元测试框架.Junit被用来开发Java类的单元测试. Junit Eclipse教程 介绍eclipse IDE 中junit的相关特性及其使用方法.在eclipse IDE中书写和运行单元测试代码的样例. Junit

Junit 4 Tutorials(Junit 4 教程) 一、Junit简介及Junit Eclipse 教程

Junit 测试框架简介 测试框架是最流行的Java单元测试框架.Junit被用来开发对Java类的单元测试.它就是一个类包,提供了各种方法来测试Java类中的方法(method). Junit 4 特性 简单的注解,提供书写Junit测试的基本特性 断言方法(Assert Methods)比较测试的方法执行结果值和期望值 @Ignore 注解,忽略测试方法或者测试类的执行 期望异常测试 超时测试 , 测试方法的执行时间 测试组件,一起运行一些测试类 参数化测试, 以不同的输入参数值测试方法 J

Junit 4 Tutorials(Junit 4 教程) 三、Junit4 断言方法

Junit 4 断言方法允许检查测试方法的期望结果值和真实返回值.Junit的org.junit.Assert类提供了各种断言方法来写junit测试.这些方法被用来检查方法的真实结果值和期望值.下列一些有用的断言方法列表: Junit 4 Assert Methods Method Description assertNull(java.lang.Object object) 检查对象是否为空 assertNotNull(java.lang.Object object) 检查对象是否不为空 as

Swift中文教程(四)--函数与闭包

原文:Swift中文教程(四)--函数与闭包 Function 函数 Swift使用func关键字来声明变量,函数通过函数名加小括号内的参数列表来调用.使用->来区分参数名和返回值的类型: 1 func greet(name: String, day: String) -> String { 2 return "Hello \(name), today is \(day)." 3 } 4 greet("Bob", "Tuesday")

Quartz教程四:Trigger

原文链接 | 译文链接 | 翻译:nkcoder 本系列教程由quartz-2.2.x官方文档翻译.整理而来,希望给同样对quartz感兴趣的朋友一些参考和帮助,有任何不当或错误之处,欢迎指正:有兴趣研究源码的同学,可以参考我对quartz-core源码的注释(进行中). 与job一样,trigger也很容易使用,但是还有一些扩展选项需要理解,以便更好地使用quartz.trigger也有很多类型,可以根据实际需要来选择. 最常用的两种trigger会分别在教程五:SimpleTrigger和教

BootStrap入门教程 (四)

上讲回顾:Bootstrap组件丰富同时具有良好可扩展性,能够很好地应用在生产环境.这些组件包括按钮(Button),导航(Navigation),缩略图( thumbnails),提醒(Alert),进度条(progress bar)等,能够很好减少前端工程师的代码量,实现更加丰富充实的页面. Bootstrap作为一套良好的前端工具,要实现现代的动态页面效果,javascript插件是必不可少的.它提供了12个基于JQuery类库的插件,包括模态窗口(Modals),滚动监控(Scrolls