欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

JUnit 3.8.2和JUnit 4.8.1学习笔记

程序员文章站 2022-04-29 18:59:18
...

 

JUnit学习笔记 2011年10月01日 环境 MyEclipse 9.0


铺垫:

 

1、单元测试(Unit Test): 

 

 是针对软件的最小模块进行正确性检验的测试工作。
 所谓最小模块,在OOP的范畴内,通常是指对象的方法。

 

2、为什么要做单元测试?

 

 进行单元测试是降低软件风险和减少维护成本的有效途径。

 

3、以前是怎么做测试的?

 

 main方法、sysout输出……

 

 优点: 快速直接

 

 缺点: 没有留下可重用的代码。
           无法进行自动化回归测试。
           增加了类的体积,代码不清晰。
           不规范、不优雅。

 

4、断言(Assertion):

 

 在软件开发中是一种常用的调试方式,很多开发语言中都支持这种机制。
 在实现中,assertion就是在程序中的一条语句,它对一个boolean表达式进行检查,
 一个正确程序必须保证这个boolean表达式的值为true;
 如果该值为false,说明程序已经处于不正确的状态下,assert将给出警告或退出。
 一般来说,assertion用于保证程序最基本、关键的正确性。
 assertion检查通常在开发和测试时开启。为了提高性能,在软件发布后,assertion检查通常是关闭的。


/*************************************************************************************************
 * 需补充关键词:
 * ① 测试驱动开发(TDD: Test-Driven Development)
 * ② Hamcrest(JUnit4.4后引入的测试框架提供了一套匹配符,可结合使用全新的断言语法:assertThat)
 ************************************************************************************************/

 


---------------------------------------我是华丽的分割线-------------------------------------------

 


正题:

 

一、JUnit简介

 

JUnit是基于面向对象构建的java单元测试框架。
JUnit是开放源代码项目,可按需要进行扩展,操作相对简单。
JUnit官方网站:http://www.junit.org
JUnit的口号:keep the bar green to keep the code clean。(PS:口号的具体含义用过JUnit后就知道了……)

 

目前JUnit官网最新版本是4.10。

我用的MyEclipse 9.0内置了3.8.2和4.8.1。

 

3.X和4.X两个版本最主要的区别是:

 

 JUnit3基于继承
 JUnit4基于注解

 

两种未通过测试的结果:
 
 Failure: 测试失败
 Error:  异常错误


二、JUnit应用

 

 

    待测类之一:

/**
 * @author Maxpin on 2011-10-01 含有四则运算方法的普通类
 */
public class Calc {
	/**
	 * 加法
	 * 
	 * @param x
	 *            数x
	 * @param y
	 *            数y
	 * @return 和
	 */
	public int add(int x, int y) {
		return x + y;
	}

	/**
	 * 减法(私有方法)
	 * 
	 * @param x
	 *            数x
	 * @param y
	 *            数y
	 * @return 差
	 */
	@SuppressWarnings("unused")
	private int subtract(int x, int y) {
		return x - y;
	}

	/**
	 * 乘法
	 * 
	 * @param x
	 *            数x
	 * @param y
	 *            数y
	 * @return 积
	 */
	public int multiply(int x, int y) {
		return x * y;
	}

	/**
	 * 除法
	 * 
	 * @param x
	 *            数x
	 * @param y
	 *            数y
	 * @return 商
	 * @throws Exception
	 *             抛出异常
	 */
	public int divide(int x, int y) throws Exception {
		if (0 == y) {
			throw new Exception("除数不能为0");
		}
		return x / y;
	}
}

 

 

1、使用JUnit 3.8.2的操作步骤及注意事项:

 

 1)为项目导入JUnit的jar包(Build path → Add Library → JUnit)。
 2)新建一个名为test的Source Folder,用于存放测试类源代码。
 3)目标类与测试类应该位于同一个包下面,这样测试类中就不必导入源代码所在的包,因为他们位于同一个包下面。
 4)测试类的命名规则:假如目标类是Calc,那么测试类应该命名为TestCalc或者是CalcTest。
 5)测试类必须要继承于TestCase父类。
 6)测试方法必须要满足public void修饰、无方法参数、方法名必须以test开头。
 7)重写setUp()方法和tearDown()方法以便于对测试数据的初始化和回归操作。
 8)执行顺序:setUp(测试方法之前)、testMethod(执行的测试方法)、tearDown(测试方法之后)。
 9)为了测试私有方法,可以使用反射机制。
 10)TestSuite(测试套件):可以将多个测试组合到一起,同时执行多个测试。

 

/**
 * 
 * @author Maxpin on 2011-10-01 含有四则运算方法的测试类
 * 
 */
public class CalcTest extends TestCase {

	private Calc calc;

	@Override
	protected void setUp() throws Exception {
		calc = new Calc();
	}

	@Override
	protected void tearDown() throws Exception {
		System.out.println("tearDown invoked");
	}

	/**
	 * 测试加法
	 */
	public void testAdd() {
		int result = calc.add(1, 2);
		assertEquals(3, result);
		System.out.println("加法测试完毕");
	}

	/**
	 * 测试减法(通过反射测试私有方法)
	 */
	public void testSubtract() {
		Class<Calc> clazz = Calc.class;
		try {
			Method method = clazz.getDeclaredMethod("subtract", new Class[] {
					Integer.TYPE, Integer.TYPE });
			// 关闭安全检查
			method.setAccessible(true);
			Object result = method.invoke(calc, new Object[] { 2, 3 });
			assertEquals(-1, result);
		} catch (Exception e) {
			e.printStackTrace();
		}
		System.out.println("减法测试完毕");
	}

	/**
	 * 测试除法
	 */
	public void testDivide() {
		Throwable th = null;
		int result = 0;
		try {
			result = calc.divide(10, 5);
		} catch (Exception e) {
			th = e;
			fail("测试失败");
		}
		assertNull(th);
		assertEquals(2, result);
		System.out.println("除法测试完毕");
	}

	/**
	 * 测试除法(期望报异常)
	 */
	public void testDivideByZero() {
		Throwable th = null;
		try {
			@SuppressWarnings("unused")
			int result = calc.divide(10, 0);
			fail("测试失败");
		} catch (Exception e) {
			th = e;
		}
		assertNotNull(th);
		assertEquals(Exception.class, th.getClass());
		assertEquals("除数不能为0", th.getMessage());
		System.out.println("除法异常测试完毕");
	}

}


/**
 * 
 * @author Maxpin on 2011-10-01 删除目录方法的测试类
 * 
 */
public class DeleteAllTest extends TestCase {
	public DeleteAllTest() {
	}

	/**
	 * 用于重复测试指定方法、指定次数时使用的构造方法
	 * 
	 * @param name
	 *            方法名
	 */
	public DeleteAllTest(String name) {
		super(name);
	}

	/**
  	* 测试删除目录的方法
  	* 构造测试数据,目录结构:
  	*       	---dir---
  	*      	   /   |    \
  	*     	 /    |     \
  	*     	 /     |      \
  	*     	/      |       \
  	*  subFile1 subFile2 file1.txt
  	*       /
  	*      /
  	*  file2.txt
  	*/	 
	public void testDeleteAll() {
		File directory = null;
		try {
			//主文件夹
			directory = new File("dir");
			directory.mkdir();
			//子文件夹1
			File subFile1 = new File(directory, "subFile1");
			//子文件夹2
			File subFile2 = new File(directory, "subFile2");
			subFile1.mkdir();
			subFile2.mkdir();
			//主文件夹下的文件
			File file1 = new File(directory, "file1.txt");
			file1.createNewFile();
			//子文件夹1下的子文件
			File file2 = new File(subFile1, "file2.txt");
			file2.createNewFile();
			DeleteAll.deleteAll(directory);
		} catch (Exception ex) {
			Assert.fail();
		}
		Assert.assertNotNull(directory);
		String[] names = directory.list();
		Assert.assertEquals(0, names.length);
		directory.delete();
		System.out.println("I/O流删除目录测试完毕");
	}
}


/**
 * 
 * @author Maxpin on 2011-10-01 测试套件类
 * 
 */
public class TestAll extends TestCase {
	/**
	 * 定义测试集
	 * 
	 * @return
	 */
	public static Test suite() {
		TestSuite suite = new TestSuite();
		// 四则运算
		suite.addTestSuite(CalcTest.class);
		// 只乘法
		suite.addTestSuite(CalcTest2.class);
		// 删除目录
		suite.addTest(new RepeatedTest(new DeleteAllTest("testDeleteAll"), 20));
		return suite;
	}
}

 

2、使用JUnit 4.8.1的操作步骤及注意事项:

 

 1)为项目导入JUnit的jar包(Build path → Add Library → JUnit)。
 2)新建一个名为test的Source Folder,用于存放测试类源代码。
 3)目标类与测试类应该位于同一个包下面,这样测试类中就不必导入源代码所在的包,因为他们位于同一个包下面。
 4)JUnit4并不要求测试类继承TestCase父类。
 5)在一个测试类中,所有被 @Test 注解所修饰的public void方法都是TestCase(测试用例),可以被JUnit所执行。
 6)规范:虽然JUnit 4并不要求测试方法名以test开头,
   但最好还是按照JUnit 3.8.2的要求那样,以test作为测试方法名的开头。
 7)在JUnit4中,通过 @Before 注解实现与JUnit 3.8.2中的setUp方法同样的功能,
    通过 @After 注解实现与JUnit 3.8中的tearDown方法同样的功能。
 8)在JUnit4中,可以使用 @BeforeClass与 @AfterClass注解修饰一个public static void  no-arg 的方法,
    这样被 @BeforeClass注解所修饰的方法会在所有测试方法执行前执行;
    被 @AfterClass注解所修饰的方法会在所有测试方法执行之后执行。
 9)注解 @Ignore可用于修饰测试类与测试方法,当修饰测试类时,表示忽略掉类中的所有测试方法;
    当修饰测试方法时,表示忽略掉该测试方法。
 10)参数化测试(Parameters):当一个测试类使用参数化运行器运行时,
     需要在类的声明处加上 @RunWith(Parameterized.class)注解,表示该类将不使用JUnit内建的运行器运行,
     而使用参数化运行器运行;在参数化运行类中提供参数的方法上要使用 @Parameters注解来修饰,
     同时在测试类的构造方法中为各个参数赋值(构造方法是由JUnit 调用的),
     最后编写测试类,它会根据参数的组数来运行测试多次。
 11)在JUnit4中,如果想要同时运行多个测试,需要使用两个注解:
     @RunWith(Suite.class)以及 @Suite.SuiteClasses(),通过这两个注解分别指定使用Suite运行器来运行测试,
     以及指定了运行哪些测试类,其中的 @SuiteClasses中可以继续指定Suite,这样JUnit会再去寻找里面的测试类,
     一直找到能够执行的TestCase并执行之。
 12)JUnit4中可以使用JUnit3的方法进行测试,而不使用注解机制,即JUnit3可以完美升级至JUnit4,但4更灵活、方便。


 

/**
 * @author Maxpin on 2011-10-01 含有四则运算方法的测试类
 * 
 */
public class CalcTest {

	private Calc calc;

	//在所有测试方法前执行一次
	@BeforeClass
	public static void globalInit() {
		System.out.println("globalInit invoked");
	}

	//在所有测试方法后执行一次
	@AfterClass
	public static void globalDestroy() {
		System.out.println("globalDestroy invoked");
	}

	//在每个测试方法前执行
	@Before
	public void setUp() {
		System.out.println("Before");
		calc = new Calc();
	}

	//在每个测试方法后执行
	@After
	public void tearDown() {
		System.out.println("After");
	}

	// 测试方法执行超过1000毫秒后算超时,测试将失败
	@Test(timeout = 1000)
	public void testAdd() {
		calc = new Calc();
		int result = calc.add(1, 2);
		assertEquals(3, result);
	}

	// 测试方法期望得到的异常类
	@Test(expected = Exception.class)
	public void testDivide() throws Exception {
		calc.divide(1, 0);
	}

	// 执行测试时将忽略掉此方法,如果用于修饰类,则忽略整个类
	@Ignore("not ready yet")
	@Test
	public void testSubtract() {
		calc = new Calc();
		int result = calc.subtract(1, 2);
		assertEquals(-1, result);
	}
}


/**
 * @author Maxpin on 2011-10-01 多组参数值测试加法运算的测试类
 * 
 *         使用参数化运行器运行
 */
@RunWith(Parameterized.class)
public class CalcParametersTest {

	Calc clac;

	private int expected; // 期望值
	private int input1; // 输入值1
	private int input2; // 输入值2

	@SuppressWarnings("rawtypes")
	// 提供参数的方法
	@Parameters
	public static Collection preparedData() {
		Object[][] obj = { { 3, 1, 2 }, { 0, -5, 5 }, { 10, 8, 2 } };
		return Arrays.asList(obj);
	}

	/**
	 * 带参构造方法
	 * 
	 * @param expected
	 *            参数期望值
	 * @param input1
	 *            参数输入值1
	 * @param input2
	 *            参数输入值2
	 */
	public CalcParametersTest(int expected, int input1, int input2) {
		this.expected = expected;
		this.input1 = input1;
		this.input2 = input2;
	}

	/**
	 * @throws java.lang.Exception
	 */
	@Before
	public void setUp() throws Exception {
		clac = new Calc();
	}

	/**
	 * Test method for {@link com.accp.junit4.Calc#add(int, int)}.
	 */
	@Test
	public void testAdd() {
		// 直接断言,会根据参数的组数来执行测试多次
		assertEquals(this.expected, clac.add(this.input1, this.input2));
	}

}


/**
 * 
 * @author Maxpin on 2011-10-01 套件测试类
 * 
 *         测试套件
 * 
 *         使用Suite运行器测试,并指定测试类
 */
@RunWith(Suite.class)
@Suite.SuiteClasses({ CalcTest.class, CalcParametersTest.class })
public class TestAll {

}


/**
 * 
 * @author Maxpin on 2011-10-01 套件测试类
 * 
 *         测试套件
 * 
 *         使用Suite运行器测试也可以嵌套Suite
 */
@RunWith(Suite.class)
@Suite.SuiteClasses({ TestAll.class, LargestorTest.class })
public class TestAllTest {

}

 


---------------------------------------我是华丽的分割线-------------------------------------------


总结:

 

 JUnit单元测试框架体积小巧,功能强大。
 及时测试显然是一种良好的开发习惯,即便测试代码量多过于被测代码量。。。
 JUnit3和JUnit4相比较而言,后者更加灵活便利。
 测试代码执行前后,被测代码的状态始终应保持一致性。


 引用某名言:单元测试不是为了证明您是对的,而是为了证明您没有错误。

 

 

 最后:附上完整版的练习实例源码,仅供学习参考交流使用。

相关标签: JUnit 单元测试