Junit使用简明教程

junit在近十年以来一直是业界测试标准框架,作为一个java开发人员,你可以不知道hibernate或者spring,但是junit是必须知道的。要编写出质量良好的软件,测试是必不可少的,许多人从毕业或培训出来在工作中从事开发工作,但是甚少甚至不进行测试,这里指的就是单元测试,一方面是对软件测试没有一个正确的概念,另一方面是存粹的单元测试比较让人抗拒。junit解决了原始的单元测试麻烦却又没有技术性的重复工作,极大的提高了单元测试的效率。

上了规模的软件会由许多个功能单一的元件组成,这些元件称为工作单元,软件的可靠性就是从这些最小粒度的工作单元共同决定的,保证每个单元功能的正确,是软件质量的基本保证,一个有良好素养的程序员,应当在开发阶段编写单元测试,确保提交的代码正确无误,这是对工作团队和自身的负责。虽然人总会犯错,但是单元测试如果覆盖足够,就可以揪出很多想当然没有的bug,在修复的这些问题之后,你会对自己的代码带有信心,而不是每次集成测试你都得提心掉胆。


如何使用junit

1、将junit相关库加入项目构建路径,使用eclipse等IDE基本都会内置junit,无需自己下载类库。


//被测试的类
public class Demo1 {
	
	public int number;
	
	public Demo1(){
		try {
			//模拟初始化需要较长时间
			Thread.sleep(2000);
			System.out.println("创建Demo1");
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
	
	//被测试的方法
	public int add(int i){
		return i++;
	}
	
	//被测试的方法
	public int sub(int i){
		return --i;
	}
	
	public int mul(int i){
		return number*i;
	}
	
	//没有写好的方法
	public int div(int i){
		return 0;
	}
	
}


2、如上图,创建对应的测试用例,并且最好放在叫test的目录下,具体看你的团队如何约定。如果是maven则相应的在test目录中。



3、当然,你自己手动创建也可以,如果是junit4则是一个普通类,如果junit3则需要继承testCase,还有类名以及方法名也有约定,比较麻烦,所以从junit4开始使用就可以了。

使用IDE提供的UI你可以勾选需要测试的类的方法即可自动生成测试方法还有注解。



自动创建好的测试类:

//静态的导入可以简化junit工具静态方法的调用
import static org.junit.Assert.*;

import org.junit.Test;

public class Demo1Test {

	
	@Test//这是一个测试方法,必须是非静态void无参
	public final void testAdd() {
		fail("Not yet implemented"); //输出表示测试失败的结果和信息
	}

	@Test
	public final void testSub() {
		fail("Not yet implemented"); // TODO
	}

	@Test
	public final void testMul() {
		fail("Not yet implemented"); // TODO
	}

	@Test
	public final void testDiv() {
		fail("Not yet implemented"); // TODO
	}
}


这个时候你可以先run as junit TEST看一下反应如何:



红色代表测试不通过,以及测试失败的方法,是不是很直观?光是这点比你使用main方法方便多了。



开始第一个测试

public class Demo1Test {

	static Demo1 demo1;
	
	
	/*@BeforeClass:
	 * 在所有测试方法执行之前执行一次,通常用于初始化一些耗时操作,如文件、网络、数据库等
	 * 例如这个demo1的创建耗时较多,如果在每个test方法里面去new,那么测试会变得很慢且冗余很多。
	 * note:不能重复,必须是静态void无参,父类方法会在子类执行之前执行
	 */
	@BeforeClass
	public static void setUpBeforeClass() throws Exception {
		demo1 = new Demo1();
	}

	/*@AfterClass:
	 * 在所有测试方法执行之后执行一次,通常用于一些收尾工作,如释放资源,输出资源等。
	 * note:不能重复,必须是静态void无参,父类方法会在子类执行之后执行
	 */
	@AfterClass
	public static void tearDownAfterClass() throws Exception {
		demo1 = null;
	}
	
	/*@Before:
	 * 在每个测试方法执行前都会执行一次,通常用于将测试数据初始化、复位等。
	 * note:不能重复,必须是非静态void无参
	 */
	@Before
	public void setUp() {
		demo1.number = 2;
	}
	
	/*@After:
	 * 在每个测试方法执行后都会执行一次,通常用于将测试数据初始化、复位等。
	 * note:不能重复,必须是非静态void无参
	 */
	@After
	public void tearDown() {
		System.out.println(demo1.number);
	}
	
	@Test
	public final void testAdd() {
		assertEquals(5, demo1.add(4));//断言测试结果,期望值5,如果实际值不相等,则结果为失败不通过。
	}

	@Test
	public final void testSub() {
		assertEquals(4, demo1.sub(5));
	}

	@Test
	public final void testMul() {
		assertEquals(10, demo1.mul(5));
	}

	/*@Ignore:
	 * 标识该方法忽略测试,可能处于某种原因(方法为实现,问题未解决等)
	 */
	@Ignore
	@Test
	public final void testDiv() {
		assertEquals(5, demo1.div(10));
	}
}


测试总结果不通过,一个忽略,一个失败,两个通过。



从输出结果来看也验证了注释的说明:



进一步的测试

public class Demo2 {
	
	public void doSomeThing(){
		for(;;){}
	}
	
	public void parseString(String i){
		int num = Integer.parseInt(i);
	}
	
	public String concat(String str1,String str2){
		return str1+str2;
	}
}
public class Demo2Test {

	/*timeout:
	 * 超过该阈值则认为测试超时不用过,通常用于测试处理耗时,死循环等
	 */
	@Test(timeout=1000)
	public final void testDoSomeThing() {
		new Demo2().doSomeThing();
	}

	/*expected:
	 * 期望测试过程会出现的异常,通常和输出参数配合,如果没有得到期望的异常则不通过
	 */
	@Test(expected=NumberFormatException.class)
	public final void testParseString() {
		new Demo2().parseString("java8");
	}

	/*
	 * 通常建议一般的测试方法为final,这样可以避免子类重写出错,或者重复的测试方法,
	 * 另外,通过编写测试方法,我们发现如果被测方法没有返回值,会对测试带来很大不便,
	 * 所以,这可以反过来指导我们编写方法都提供返回值,如果一个被测试类里面几个核心的方法都没有返回值,
	 * 那么,你需要检查被测试方法是否粒度过大,或者违背单一职责。
	 */
	@Test
	public final void testConcat() {
		String str = new Demo2().concat("BL", " UE");
		assertEquals("BLUE", str);
	}
}


parseString测试抛出了预期的异常,通过,DoSomeThing测试由于存在死循环导致超时,不通过,Concat经测试存在bug,如果参数有空格就不符合预期。


参数化测试

当需要进行多组参数测试的时候,如上面的concat,你可能需要编写多个测试方法应对不同的输入,这显然很不人性化,因此,使用参数化测试来做。

我们对测试类进行修改:

/*@RunWith:
 * junit实际上根据runner来决定采用哪种测试策略来执行测试方法,如果没有显示注解声明,
 * 则会使用默认的runner,如果我们要使用参数化的测试模式,那么需要显示的定义使用的runner
 */
@RunWith(Parameterized.class)
public class Demo2Test {
	
	/*@Parameters:
	 * 当使用参数化测试的时候,通过该注解标注返回(结果:参数)的集合,
	 * 这些键值对会被输入到测试方法自动进行断言
	 * note:必须是静态无参的,返回类型是数组或集合,每换一组参数都会执行所有的测试方法,因此你最好有所准备
	 */
	@Parameters
	public static Object[] testData(){
		return new Object[][]{{"BLUE","BL"," UE"},
								{"Google","Goog","le"},
								{"Anna"," An","na"}};
	}
	
	//定义参数成员变量
	String result;
	String param1;
	String param2;
	
	//提供构造方法设置参数,注意参数顺序,顺序是按着上面的数组中元素的顺序赋值的
	public Demo2Test(String result,String param1, String param2) {
		this.result = result;
		this.param1 = param1;
		this.param2 = param2;
	}
@Test
	public final void testConcat() {
		String str = new Demo2().concat(param1, param2);
		assertEquals(result, str);
	}


将不进行参数化测试的方法忽略,否则会重复多遍,对于参数队列的每一组,结果如下:


打包测试

实际中团队开发,我们不太可能一个个测试类去点击执行,打包测试是一种效率更高的办法,将需要执行的测试类批量的执行。但是我觉得这个套件比较鸡肋,没有达到所谓的打包效果,因为注解的值只能是类型的字面值,不能通过工具来转换,也就限制了运用,特别是多个包中的多个测试类的情况下很不方便,可以再考虑使用ant或maven工具来做。

注意runner

@RunWith(Suite.class)
@SuiteClasses({Demo1Test.class,Demo2Test.class})
public class TestAll {


}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值