1、會用Spring測試套件的好處java
在開發基於Spring的應用時,若是你還直接使用Junit進行單元測試,那你就錯過了Spring爲咱們所提供的饕餮大餐了。使用Junit直接進行單元測試有如下四大不足:web
1)致使屢次Spring容器初始化問題spring
根據JUnit測試方法的調用流程,每執行一個測試方法都會建立一個測試用例的實例並調用setUp()方法。因爲通常狀況下,咱們在setUp()方法中初始化Spring容器,這意味着若是測試用例有多少個測試方法,Spring容器就會被重複初始化屢次。雖然初始化Spring容器的速度並不會太慢,但因爲可能會在Spring容器初始化時執行加載Hibernate映射文件等耗時的操做,若是每執行一個測試方法都必須重複初始化Spring容器,則對測試性能的影響是不容忽視的;數據庫
使用Spring測試套件,Spring容器只會初始化一次session
2)須要使用硬編碼方式手工獲取Beanapp
在測試用例類中咱們須要經過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包下的若干類組成,使用簡單快捷,方便上手。
2、使用方法
1)基本用法
package com.test; import javax.annotation.Resource; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = { "classpath:config/applicationContext-*.xml", "classpath:services/ext/service-*.xml" }) public class UserServiceTest { @Resource private IUserService userService; @Test public void testAddOpinion1() { userService.downloadCount(1); System.out.println(1); } @Test public void testAddOpinion2() { userService.downloadCount(2); System.out.println(2); } }
@RunWith(SpringJUnit4ClassRunner.class) 用於配置spring中測試的環境
@ContextConfiguration(locations = { "classpath:config/applicationContext-*.xml", "classpath:services/ext/service-*.xml" })用於指定配置文件所在的位置
@Resource注入Spring容器Bean對象,注意與@Autowired區別
2)事務用法
package com.test; import javax.annotation.Resource; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.test.annotation.Rollback; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.transaction.TransactionConfiguration; import org.springframework.transaction.annotation.Transactional; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = { "classpath:config/applicationContext-*.xml", "classpath:services/ext/service-*.xml" }) @Transactional @TransactionConfiguration(transactionManager = "transactionManager") //@TransactionConfiguration(transactionManager = "transactionManager", defaultRollback = true) public class UserServiceTest { @Resource private IUserService userService; @Test // @Transactional public void testAddOpinion1() { userService.downloadCount(1); System.out.println(1); } @Test @Rollback(false) public void testAddOpinion2() { userService.downloadCount(2); System.out.println(2); } }
@TransactionConfiguration(transactionManager="transactionManager")讀取Spring配置文件中名爲transactionManager的事務配置,defaultRollback爲事務回滾默認設置。該註解是可選的,可以使用@Transactional與@Rollback配合完成事務管理。固然也能夠使用@Transactional與@TransactionConfiguration配合。
@Transactional開啓事務。可放到類或方法上,類上做用於全部方法。
@Rollback事務回滾配置。只能放到方法上。
3)繼承AbstractTransactionalJUnit4SpringContextTests
package com.test; import javax.annotation.Resource; import org.junit.Test; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.AbstractTransactionalJUnit4SpringContextTests; import org.springframework.test.context.transaction.TransactionConfiguration; @ContextConfiguration(locations = { "classpath:config/applicationContext-*.xml", "classpath:services/ext/service-*.xml" }) @TransactionConfiguration(transactionManager = "transactionManager", defaultRollback = false) public class UserServiceTest extends AbstractTransactionalJUnit4SpringContextTests { @Resource private IUserService userService; @Test public void testAddOpinion1() { userService.downloadCount(1); System.out.println(1); } @Test public void testAddOpinion2() { userService.downloadCount(2); System.out.println(2); } }
AbstractTransactionalJUnit4SpringContextTests:這個類爲咱們解決了在web.xml中配置OpenSessionInview所解決的session生命週期延長的問題,因此要繼承這個類。該類已經在類級別預先配置了好了事物支持,所以沒必要再配置@Transactional和@RunWith
4)繼承
package com.test; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.AbstractTransactionalJUnit4SpringContextTests; import org.springframework.test.context.transaction.TransactionConfiguration; @ContextConfiguration(locations = { "classpath:config/applicationContext-*.xml", "classpath:services/ext/service-*.xml" }) @TransactionConfiguration(transactionManager = "transactionManager") public class BaseTestCase extends AbstractTransactionalJUnit4SpringContextTests { }
package com.test; import javax.annotation.Resource; import org.junit.Test; import org.springframework.test.annotation.Rollback; public class UserServiceTest extends BaseTestCase { @Resource private IUserService userService; @Test public void testAddOpinion1() { userService.downloadCount(1); System.out.println(1); } @Test @Rollback(false) public void testAddOpinion2() { userService.downloadCount(2); System.out.println(2); } }
5)綜合
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration @TransactionConfiguration @Transactional public class PersonDaoTransactionUnitTest extends AbstractTransactionalJUnit4SpringContextTests { final Logger logger = LoggerFactory.getLogger(PersonDaoTransactionUnitTest.class); protected static int SIZE = 2; protected static Integer ID = new Integer(1); protected static String FIRST_NAME = "Joe"; protected static String LAST_NAME = "Smith"; protected static String CHANGED_LAST_NAME = "Jackson"; @Autowired protected PersonDao personDao = null; /** * Tests that the size and first record match what is expected before the transaction. */ @BeforeTransaction public void beforeTransaction() { testPerson(true, LAST_NAME); } /** * Tests person table and changes the first records last name. */ @Test public void testHibernateTemplate() throws SQLException { assertNotNull("Person DAO is null.", personDao); Collection<Person> lPersons = personDao.findPersons(); assertNotNull("Person list is null.", lPersons); assertEquals("Number of persons should be " + SIZE + ".", SIZE, lPersons.size()); for (Person person : lPersons) { assertNotNull("Person is null.", person); if (ID.equals(person.getId())) { assertEquals("Person first name should be " + FIRST_NAME + ".", FIRST_NAME, person.getFirstName()); assertEquals("Person last name should be " + LAST_NAME + ".", LAST_NAME, person.getLastName()); person.setLastName(CHANGED_LAST_NAME); personDao.save(person); } } } /** * Tests that the size and first record match what is expected after the transaction. */ @AfterTransaction public void afterTransaction() { testPerson(false, LAST_NAME); } /** * Tests person table. */ protected void testPerson(boolean beforeTransaction, String matchLastName) { List<Map<String, Object>> lPersonMaps = simpleJdbcTemplate.queryForList("SELECT * FROM PERSON"); assertNotNull("Person list is null.", lPersonMaps); assertEquals("Number of persons should be " + SIZE + ".", SIZE, lPersonMaps.size()); Map<String, Object> hPerson = lPersonMaps.get(0); logger.debug((beforeTransaction ? "Before" : "After") + " transaction. " + hPerson.toString()); Integer id = (Integer) hPerson.get("ID"); String firstName = (String) hPerson.get("FIRST_NAME"); String lastName = (String) hPerson.get("LAST_NAME"); if (ID.equals(id)) { assertEquals("Person first name should be " + FIRST_NAME + ".", FIRST_NAME, firstName); assertEquals("Person last name should be " + matchLastName + ".", matchLastName, lastName); } } }
@BeforeTransaction在事務以前執行
@AfterTransaction在事務以後執行
@NotTransactional不開啓事務
好了,本篇做爲Junit補充就說到這裏了,但願你們多多分享經驗哦。