1. 使用 Junit 的最佳实践:
1)新建一个名为 test 的 source folder, 用于存放测试类资源代码。
2)目标类与测试类应该位于同一个包下面,这样测试类中就不必导入源代码所在的包,因为他们位于同一个包下面。
3)测试类的命名规则: 假如目标类是 Calculator,那么测试类应该命名为TestCalculator或者是 CalculatorTest。
2. Junit 的口号: keep the bar green to keep the code clean.
3. Junit: 单元测试不是为了证明您是对的,而是为了证明您没有错误。
4. 在junit 3.8中,测试方法需要满足如下原则:
1.) public 的
2.) void 的
3.) 无方法参数
4. )方法名称必须以test开头
5. )测试类必须继承于TestCase 父类
5. 例子:
package com.bob.junit;
/**
* 加减乘除类
*/
public class Calculator {
public int add(int a, int b){
return a + b;
}
public int subtracta(int a, int b){
return a - b;
}
public int multiply(int a , int b){
return a * b;
}
public int divide(int a, int b){
return a / b;
}
// public static void main(String[] args) {
// Calculator cal = new Calculator();
// int result = cal.add(1, 2);
// System.out.println("期望结果是3:" + "实际结果是:" + result); //原始的单元测试方法
// }
}
package com.bob.junit;
import junit.framework.Assert;
import junit.framework.TestCase;
/**
* 测试Calculator类。
*在junit 3.8中,测试方法需要满足如下原则:
*1. public 的
*2. void 的
*3. 无方法参数
*4. 方法名称必须以test开头
*5. 测试类必须继承于TestCase 父类
*/
public class CalculatorTest extends TestCase {
private Calculator cal;
@Override
public void setUp() throws Exception { //可以扩大访问权限
cal = new Calculator();
}
@Override
protected void tearDown() throws Exception {
// TODO Auto-generated method stub
super.tearDown();
}
public void testAdd(){
// Calculator cal = new Calculator(); //因为测试类包名与目标类相同所以无需导入目标类包import com.bob.junit
int result = cal.add(1, 2);
Assert.assertEquals(3, result);
}
public void testSubtract(){
// Calculator cal = new Calculator();
int result = cal.subtracta(1, 2);
Assert.assertEquals(-1, result);
}
public void testMultiply(){
// Calculator cal = new Calculator();
int result = cal.multiply(2, 3);
Assert.assertEquals(6, result);
}
public void testDivide(){
// Calculator cal = new Calculator();
int result = cal.divide(6, 2);
Assert.assertEquals(3, result);
}
// public void testDivideDivideByZero(){
// Calculator cal = new Calculator();
// int result = cal.divide(6, 0);
// }
}
注意:
1) 测试用例之间一定要保持完全的独立性,不允许出现任何的依赖关系。
2)我们不能依赖于测试方法的执行顺序
观察上面的程序会发现每行都需要 new Calculator() 对象,写代码中有一个原则:DRY(Don't Repeat Yourself),所以使用junit中提供的方法解决。
6. 关于 setUP 与 tearDown 方法的执行顺序:
setUp --> testAddXXX --> tearDown 以此类推。在每一个测试用例执行之前都会先执行setUp,然后执行测试方法,每一个测试用例执行之后都会执行tearDown。
7. 例子:关于异常测试的常用策略
package com.bob.junit;
/**
* 加减乘除类
*/
public class Calculator {
public int add(int a, int b){
return a + b;
}
public int subtracta(int a, int b){
return a - b;
}
public int multiply(int a , int b){
return a * b;
}
public int divide(int a, int b) throws Exception{
if(0 == b){
throw new Exception("除数不能为0");
}
return a / b;
}
// public static void main(String[] args) {
// Calculator cal = new Calculator();
// int result = cal.add(1, 2);
// System.out.println("期望结果是3:" + "实际结果是:" + result); //原始的单元测试方法
// }
}
package com.bob.junit;
import junit.framework.Assert;
import junit.framework.TestCase;
/**
* 测试Calculator类。
*在junit 3.8中,测试方法需要满足如下原则:
*1. public 的
*2. void 的
*3. 无方法参数
*4. 方法名称必须以test开头
*5. 测试类必须继承于TestCase 父类
*/
public class CalculatorTest extends TestCase {
private Calculator cal;
@Override
public void setUp() throws Exception { //可以扩大访问权限
cal = new Calculator();
}
@Override
protected void tearDown() throws Exception {
// TODO Auto-generated method stub
super.tearDown();
}
public void testAdd(){
// Calculator cal = new Calculator(); //因为测试类包名与目标类相同所以无需导入目标类包import com.bob.junit
int result = cal.add(1, 2);
Assert.assertEquals(3, result);
}
public void testSubtract(){
// Calculator cal = new Calculator();
int result = cal.subtracta(1, 2);
Assert.assertEquals(-1, result);
}
public void testMultiply(){
// Calculator cal = new Calculator();
int result = cal.multiply(2, 3);
Assert.assertEquals(6, result);
}
public void testDivide(){
// Calculator cal = new Calculator();
int result = 0;
try {
result = cal.divide(6, 2);
} catch (Exception e) {
e.printStackTrace();
Assert.fail();
}
Assert.assertEquals(3, result);
}
public void testDivideDivideByZero(){
Throwable tx = null;
try {
cal.divide(6, 0); //这行代码应该需要抛异常,下面的应该不会执行
Assert.fail("测试失败"); //不执行到这里才是正常的
} catch (Exception ex) {
tx = ex; //将抛出的异常赋给异常的父类
}
Assert.assertEquals(Exception.class, tx.getClass()); //比较预期抛出的异常类型与实际类型是否相符
Assert.assertEquals("除数不能为0", tx.getMessage()); //比较预期抛出的异常内容与实际抛出的是否相符
}
}
通常情况下我们将Assert.fail(); 放在我们认为程序不可能到达的地方,如果到达了那么测试肯定是失败了。
8. 使用 Junit 原生方式运行测试用例
1. 编写Calculator.java和CalculatorTest类;
2. 解压缩“junit.jar” 包然后,并将解压出来的junit文件夹与编写的类放置在相同目录下(为了方便);
3. 在测试类中加入main方法是用junit提供的“junit.textui.TestRunner.run()”或“junit.awtui.TestRunner.run()”等方法运行测试类;
4. Win+R使用命令行方式编译运行目标类和测试类。
import junit.framework.Assert;
import junit.framework.TestCase;
/**
* 测试Calculator类。
*在junit 3.8中,测试方法需要满足如下原则:
*1. public 的
*2. void 的
*3. 无方法参数
*4. 方法名称必须以test开头
*5. 测试类必须继承于TestCase 父类
*/
public class CalculatorTest extends TestCase {
private Calculator cal;
@Override
public void setUp() throws Exception { //可以扩大访问权限
cal = new Calculator();
}
@Override
protected void tearDown() throws Exception {
// TODO Auto-generated method stub
super.tearDown();
}
public void testAdd(){
// Calculator cal = new Calculator(); //因为测试类包名与目标类相同所以无需导入目标类包import com.bob.junit
int result = cal.add(1, 2);
Assert.assertEquals(3, result);
}
public void testSubtract(){
// Calculator cal = new Calculator();
int result = cal.subtracta(1, 2);
Assert.assertEquals(-1, result);
}
public void testMultiply(){
// Calculator cal = new Calculator();
int result = cal.multiply(2, 3);
Assert.assertEquals(6, result);
}
public void testDivide(){
// Calculator cal = new Calculator();
int result = 0;
try {
result = cal.divide(6, 2);
} catch (Exception e) {
e.printStackTrace();
Assert.fail();
}
Assert.assertEquals(3, result);
}
public void testDivideDivideByZero(){
Throwable tx = null;
try {
cal.divide(6, 0); //这行代码应该需要抛异常,下面的应该不会执行
Assert.fail("测试失败"); //不执行到这里才是正常的
} catch (Exception ex) {
tx = ex; //将抛出的异常赋给异常的父类
}
Assert.assertEquals(Exception.class, tx.getClass()); //比较预期抛出的异常类型与实际类型是否相符
Assert.assertEquals("除数不能为0", tx.getMessage()); //比较预期抛出的异常内容与实际抛出的是否相符
}
//以Junit的方式运行测试用例
public static void main(String[] args) {
junit.textui.TestRunner.run(CalculatorTest.class); //以文本命令行方式显示结果
junit.awtui.TestRunner.run(CalculatorTest.class); //以awt界面方式运行
}
}
9. 使用反射测试类的私有方法
package com.bob.junit;
public class Calculator2 {
private int add(int a, int b){
return a + b;
}
}
package com.bob.junit;
import java.lang.reflect.Method;
import junit.framework.Assert;
import junit.framework.TestCase;
//使用反射测试私有方法
public class Calculator2Test extends TestCase {
public void testAdd() {
Calculator2 cal2 = new Calculator2();
Class<Calculator2> clazz = Calculator2.class;
try {
Method method = clazz.getDeclaredMethod("add", new Class[] {
Integer.TYPE, Integer.TYPE });
method.setAccessible(true); //压制编译器的默认检查访问权限
Object result = method.invoke(cal2, new Object[]{2, 3});
Assert.assertEquals(5, result);
} catch (Exception e) {
Assert.fail();
}
}
}
10. TestSuite(测试套件):可以将多个测试组合到一起,同时执行多个测试。
package com.bob.junit;
import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;
//自动化运行所需要的测试类
public class TestAll extends TestCase {
//定义一个 static 的测试方法,返回一个Test对象
public static Test suite(){
TestSuite suite = new TestSuite();
suite.addTestSuite(CalculatorTest.class); //添加需要执行的测试类
suite.addTestSuite(Calculator2Test.class);
return suite;
}
}
如何运行指定方法N次:
可以在测试类中加入带参数的构造方法,然后使用junit的RepeatedTest 类进行多次运行同一个测试方法。
public CalculatorTest(String name){
super(name);
}
suite.addTest(new RepeatedTest(new CalculatorTest("testAdd"), 20));
//运行指定方法20次
11. 对于测试类来说,如果一个测试类中有5个测试方法,那么 JUnint 就会创建5个城市类的对象,每一个对象只会调用一个测试方法(为了符合命令模式的要求),在添加方法之前,需要首先判断测试方法是否满足public, void, no-arg,no-return这些条件,如果满足则添加到集合当中准备作为测试方法来去执行。