Junit--spring 應用

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補充就說到這裏了,但願你們多多分享經驗哦。

相關文章
相關標籤/搜索