码迷,mamicode.com
首页 > 其他好文 > 详细

Junit4 单元测试

时间:2015-11-15 20:46:52      阅读:250      评论:0      收藏:0      [点我收藏+]

标签:单元测试   junit   

最近公司一直在鼓励写单元测试,故最近自己也整理了些比较常用的单元测试用法,在这里跟大家分享!


以下便是我们经常写的一个测试类,那么其中的一些内容我们是否完全都理解呢,下面我来给大家介绍下:

package com.lyancafe.csr.bo;

import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;

import junit.framework.Assert;

import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;

import com.lyancafe.common.Pagination;
import com.lyancafe.core.dao.CityDao;
import com.lyancafe.core.model.City;
import com.lyancafe.exception.CityExistException;


public class CityBoImplTest {
	private CityBoImpl cityBoImpl;
	private CityDao cityDao;
	
	@Before
	public void setUp() throws Exception {
		cityBoImpl = new CityBoImpl();
		cityDao = mock(CityDao.class);

		setPropertyByReflect(cityBoImpl, "cityDao", cityDao);
	}
	
	@Test
	public void testCreateCity() {
		City city = createCity();
		when(cityDao.queryCount(Mockito.anyMapOf(String.class, Object.class))).thenReturn(0);
		when(cityDao.createCity(Mockito.any(City.class))).thenReturn(1);
		try {
			cityBoImpl.createCity(city);
		} catch (CityExistException e) {
			Assert.assertTrue(false);
		}
		verify(cityDao, times(1)).queryCount(Mockito.anyMapOf(String.class, Object.class));
		verify(cityDao, times(1)).createCity(Mockito.any(City.class));
	}
	
	@Test
	public void testRenameCitySuccessfully() {
		City city = createCity();
		//when(cityDao.queryCount(Mockito.anyMapOf(String.class, Object.class))).thenReturn(0);
		when(cityDao.update(Mockito.any(City.class))).thenReturn(1);
		
		try {
			cityBoImpl.renameCity(city);
		} catch (CityExistException e) {
			Assert.assertTrue(false);
		}
		
		//verify(cityDao, times(1)).queryCount(Mockito.anyMapOf(String.class, Object.class));
		verify(cityDao, times(1)).update(Mockito.any(City.class));
	}
	
	@Test
	public void testRenameCityToAnUsedName() {
		City city = createCity();
		//when(cityDao.queryCount(Mockito.anyMapOf(String.class, Object.class))).thenReturn(1);
		when(cityDao.update(Mockito.any(City.class))).thenReturn(1);
		try {
			cityBoImpl.renameCity(city);
			// should get exception
			//Assert.assertTrue(false);
		} catch (CityExistException e) {
		}
		//verify(cityDao, times(1)).queryCount(Mockito.anyMapOf(String.class, Object.class));
		verify(cityDao, times(1)).update(Mockito.any(City.class));
	}
	
	@Test
	public void testOpenCity() {
		City city = createCity();
		when(cityDao.openCity(Mockito.any(City.class))).thenReturn(1);
		cityBoImpl.openCity(city);
		verify(cityDao, times(1)).openCity(Mockito.any(City.class));
	}
	
	@Test
	public void testCloseCity() {
		City city = createCity();
		when(cityDao.closeCity(Mockito.any(City.class))).thenReturn(1);
		cityBoImpl.closeCity(city);
		verify(cityDao, times(1)).closeCity(Mockito.any(City.class));
	}
	
	@Test
	public void testFindActiveCity() {
		Pagination<City> pagination = new Pagination<City>();
		pagination.setItemPerPage(1);
		pagination.setCurPage(1);
		
		List<City> cityList = createCityList();
		
		when(cityDao.queryCount(Mockito.anyMapOf(String.class, Object.class))).thenReturn(cityList.size());
		when(cityDao.queryCity(Mockito.anyMapOf(String.class, Object.class))).thenReturn(cityList);
		
		pagination = cityBoImpl.findActiveCityByPage(pagination);
		
		verify(cityDao, times(1)).queryCount(Mockito.anyMapOf(String.class, Object.class));
		verify(cityDao, times(1)).queryCity(Mockito.anyMapOf(String.class, Object.class));
		
		Assert.assertEquals(pagination.getTotalCount(), cityList.size());
		Assert.assertTrue(pagination.hasNext());
	}
	
	@Test
	public void testFindInactiveCity() {
		Pagination<City> pagination = new Pagination<City>();
		pagination.setItemPerPage(1);
		pagination.setCurPage(1);
		
		List<City> cityList = createCityList();
		
		when(cityDao.queryCount(Mockito.anyMapOf(String.class, Object.class))).thenReturn(cityList.size());
		when(cityDao.queryCity(Mockito.anyMapOf(String.class, Object.class))).thenReturn(cityList);
		
		pagination = cityBoImpl.findInactiveCityByPage(pagination);
		
		verify(cityDao, times(1)).queryCount(Mockito.anyMapOf(String.class, Object.class));
		verify(cityDao, times(1)).queryCity(Mockito.anyMapOf(String.class, Object.class));
		
		Assert.assertEquals(pagination.getTotalCount(), cityList.size());
		Assert.assertTrue(pagination.hasNext());
	}
	
	private City createCity() {
		return createCity("any city name");
	}
	
	private City createCity(String cityName) {
		City city = new City();
		city.setName(cityName);
		return city;
	}
	
	private List<City> createCityList() {
		List<City> cityList = new ArrayList<City>();
		cityList.add(createCity("Shanghai"));
		cityList.add(createCity("Beijing"));
		cityList.add(createCity("Shenzhen"));
		cityList.add(createCity("Guangzhou"));
		cityList.add(createCity("Hangzhou"));
		return cityList;
	}
	
	public void setPropertyByReflect(Object target, String name, Object value) throws Exception {
		Field field = target.getClass().getDeclaredField(name);
		field.setAccessible(true);
		field.set(target, value);
	}

} 


一,测试方法的声明


在测试类中,并不是每一个方法都是用于测试的,你必须使用“标注”来明确表明哪些是测试方法。“标注”也是JDK5的一个新特性,用在此处非常恰当。我们可以看到,在某些方法的前有@Before@Test@Ignore等字样,这些就是标注,以一个“@”作为开头。这些标注都是JUnit4自定义的,熟练掌握这些标注的含义非常重要。


1,简单的测试类

  @Test
	 public void testUpdate() {
		 DeliveryArea carryArea = carryAreaBo.findCarryAreaById(8);
		 if (carryArea != null) {
			 carryArea.setName("why");
			 carryArea.setCenterAddress("myaddress");
			 carryArea.setCenterLatitude(121.12345678901234);
			 carryArea.setCenterLongitude(234.09876543214321);
			 carryArea.setRadius(14.1234);
			 carryAreaBo.updateCarryArea(carryArea);
			 System.out.println(carryArea.getCenterLatitude());
			 System.out.println(carryArea.getCenterLongitude());
  			 System.out.println(carryArea.toString());
		}
 	}

2,忽略测试某些尚未完成的方法

  @Ignore
	 @Test
	 public void testUpdate() {
		 DeliveryArea carryArea = carryAreaBo.findCarryAreaById(8);
		 if (carryArea != null) {
			 carryArea.setName("why");
			 carryArea.setCenterAddress("myaddress");
			 carryArea.setCenterLatitude(121.12345678901234);
			 carryArea.setCenterLongitude(234.09876543214321);
			 carryArea.setRadius(14.1234);
 			carryAreaBo.updateCarryArea(carryArea);
			 System.out.println(carryArea.getCenterLatitude());
			 System.out.println(carryArea.getCenterLongitude());
 			System.out.println(carryArea.toString());
		}
	 }

3,Fixture(固定代码段的执行)

Fixture的含义就是“在某些阶段必然被调用的代码”。


其一:


比如,如果我们只声明了一个Calculator对象,他的初始值是0,但是测试完加法操作后,他的值就不是0了;接下来测试减法操作,就必然要考虑上次加法操作的结果。这绝对是一个很糟糕的设计!我们非常希望每一个测试都是独立的,相互之间没有任何耦合度。因此,我们就很有必要在执行每一个测试之前,对Calculator对象进行一个“复原”操作,以消除其他测试造成的影响。因此,“在任何一个测试执行之前必须执行的代码”就是一个Fixture,我们用@Before来标注它,如前面例子所示:


      @Before
      public void setUp() throws Exception {
           calculator.clear();
      }


这里不在需要@Test标注,因为这不是一个test,而是一个Fixture。同理,如果“在任何测试执行之后需要进行的收尾工作”也是一个Fixture,使用@After来标注。由于本例比较简单,没有用到此功能。


其二:


有一个类是负责对大文件(超过500兆)进行读写,他的每一个方法都是对文件进行操作。换句话说,在调用每一个方法之前,我们都要打开一个大文件并读入文件内容,这绝对是一个非常耗费时间的操作。如果我们使用@Before@After,那么每次测试都要读取一次文件,效率及其低下。这里我们所希望的是在所有测试一开始读一次文件,所有测试结束之后释放文件,而不是每次测试都读文件。JUnit的作者显然也考虑到了这个问题,它给出了@BeforeClass @AfterClass两个Fixture来帮我们实现这个功能。从名字上就可以看出,用这两个Fixture标注的函数,只在测试用例初始化时执行@BeforeClass方法,当所有测试执行完毕之后,执行@AfterClass进行收尾工作。在这里要注意一下,每个测试类只能有一个方法被标注为@BeforeClass @AfterClass,并且该方法必须是PublicStatic的。


二:使用Mockito


介绍:


JUnit 是单元测试框架。Mockito 与 JUnit 不同,并不是单元测试框架(这方面 JUnit 已经足够好了),它是用于生成模拟对象或者直接点说,就是”假对象“的工具。两者定位不同,所以一般通常的做法就是联合 JUnit + Mockito 来进行测试。


举例:

List mock = mock( List.class );  
when( mock.get(0) ).thenReturn( 1 );  
assertEquals( "预期返回1", 1, mock.get( 0 ) );// mock.get(0) 返回 1


其 中 mock 是模拟 List 的对象,拥有 List 的所有方法和属性。when(xxxx).thenReturn(yyyy); 是指定当执行了这个方法的时候,返回 thenReturn 的值,相当于是对模拟对象的配置过程,为某些条件给定一个预期的返回值。可以理解为对于我们要测试的方法中,有需要调用mock的对象的对应方法,当调用到此方法时,对于返回值的设定。


Assert 说明


junit中的assert方法全部放在Assert类中,下面简单介绍两个:


1.assertTrue/False([String message,]boolean condition);
    用来查看变量是是否为false或true,如果assertFalse()查看的变量的值是false则测试成功,如果是true则失败,assertTrue()与之相反;

2.fail([String message,]);
    直接用来抛出错误。

3.assertEquals([String message,]Object expected,Object actual);
    判断是否相等,可以指定输出错误信息。
    第一个参数是期望值,第二个参数是实际的值。
    这个方法对各个变量有多种实现



验证Verify

前面提到的 when(……).thenReturn(……) 属于状态测试,某些时候,测试不关心返回结果,而是侧重方法有否被正确的参数调用过,这时候就应该使用 验证方法了。从概念上讲,就是和状态测试所不同的“行为测试”了。

一旦使用 mock() 对模拟对象打桩,意味着 Mockito 会记录着这个模拟对象调用了什么方法,还有调用了多少次。最后由用户决定是否需要进行验证,即 verify() 方法。


mockedList.add("one");  
mockedList.add("two");  
verify(mockedList).add("one"); // 如果times不传入,则默认是1  
verify 内部跟踪了所有的方法调用和参数的调用情况,然后会返回一个结果,说明是否通过。

若调用成功,则程序正常运行,反之则会报告:Wanted but not invoked :verify(mockedList).add("one")


Map mock = Mockito.mock( Map.class );  
when( mock.get( "city" ) ).thenReturn( "广州" );  
// 关注参数有否传入  
verify(mock).get( Matchers.eq( "city" ) );  
// 关注调用的次数  
verify(mock, times( 2 ));     

也就是说,这是对历史记录作一种回溯校验的处理。

再来一个例子相信大家就明白了

//首先要importMockito.

import static org.mockito.Mockito.*;

//mock creation

List mockedList = mock(List.class);

//using mock object

mockedList.add("one");

mockedList.clear();

//验证add方法是否在前面被调用了一次,且参数为“one”。clear方法同样。

verify(mockedList).add("one");

verify(mockedList).clear();

//下面的验证会失败。因为没有调用过add("two")。

verify(mockedList).add("two");


原文中的一句话很重要:Once created, mock will remember all interactions.所以mockito知道前面是否调用过某方法。


总结:


以上只是对于单元测试的常用方法做了简单的说明,但是junit4还有很多强大的功能,例如:限时测试,测试异常,参数化测试,以及打包测试等,这些功能也同样很实用,还需要大家慢慢挖掘。


附:JUnit4 学习







版权声明:本文为博主原创文章,未经博主允许不得转载。

Junit4 单元测试

标签:单元测试   junit   

原文地址:http://blog.csdn.net/hejingyuan6/article/details/49851109

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!