JUnit单元测试
JUnit简介
由Erich Gamma和Kent Beck编写
回归测试框架
开源的Java测试框架
属于白盒测试,由开发人员来编写
主要用来判断程序的执行结果与自己期望的结果是否一致
很重要的一个方面是测试用例(Test Case)
不是证明您是对的,而是为了证明您没有错误
JUnit单元测试优点
极限编程(最大化的开发效率)
极限编程要求在编写代码之前先写测试,这样可以强制你在写代码之前好好的思考代码的功能和逻辑
否则编写的代码很不稳定,那么你需要同时维护测试代码和实际代码,这个工作量就会大大增加
因此在极限编程中,基本过程是这样的:构思-编写测试代码-编写代码-测试
而且编写测试和编写代码都是增量式的,写一点测一点
在编写以后的代码中如果发现问题可以较快的追踪到问题的原因,减小回归错误的纠错难度
JUnit3.8基于反射,JUnit4.0基于注解
所以JUnit3测试类必须继承TestCase,JUnit4测试类不需要继承TestCase
使用JUint的规定
对测试包的规定
测试包必须和src是同级目录,并且是Source Folder,否则不会被编译
测试类和被测试类虽然不在一个Source Folder中,
但包名必须一致,这样才能保证编译之后的字节码文件在一个文件夹中
对测试类名的规定
如果被测试类类名为Calculator,则测试类类名为CalculatorTest
测试类必须保持完全独立性,不允许出现任何的依赖关系
对测试方法的规定
public
void
不能有返回值
被测试方法名为addition,则测试方法名为testAddition
测试方法必须是独立的,不允许有任何的依赖关系
每执行一个测试方法就会生成一个测试类实例
比如一个测试类中有一个成员变量 int count=0
在setUp()方法中打印++count
如果有三个测试方法,执行后会打印三个1
JUnit测试类中每个测试方法都是一个测试用例(test case)
JUnit4注解说明
@Before和@After
@Before和@After修饰的方法相当于JUnit3中的setUp()和tearDown()
被@Before修饰的方法为初始化方法,每测试一个方法先执行这个方法
被@After修饰的方法为释放方法,每测试一个方法最后都要执行这个方法
@Test
被@Test修饰的方法为测试用例
被@Test(timeout = 200)修饰的方法,还要测试是否在200毫秒之内执行完毕
被@Test(expected = Exception.class)修饰的方法,还要测试是否抛出的异常是Exception
@BeforeClass和@AfterClass
全局注解
@BeforeClass和@AfterClass修饰的方法必须是静态的
@BeforeClass全局初始化只执行一次,最先执行
@AfterClass全局销毁只执行一次,最后执行
@Ignore
可以用于测试类或者测试方法上表示忽略,不进行测试
为项目配置JUnit环境
第一步:增加JUnit类库,点击项目右键->Build Path->Add Library->选择JUnit
第二步:为src增加一个测试包,点击项目右键->new->Source Folder 名称为test
MyEclipse IDE自动创建测试类
第一步:创建一个和src同级名称为test的Source Folder
第二步:选择src下需要测试的类右键->new->Junit Test Case
JUnit案例
/* 被测试类 */
package com.itlwc;
public class Calculator {
public int addition(int a, int b) {
return a + b;
}
}
JUnit3测试类CalculatorTest
package com.itlwc;
import junit.framework.TestCase;
public class CalculatorTest extends TestCase {
private Calculator c = null;
// 初始化方法(每测试一个方法先执行setUp方法)
@Override
public void setUp() throws Exception {
c = new Calculator();
}
public void testAddition() {
// 真实的值
int actual = c.addition(5, 3);
// 期望的值
int expected = 8;
assertEquals(expected, actual);
}
// 释放方法(每测试一个方法最后都要执行tearDown方法)
@Override
public void tearDown() throws Exception {
c = null;
}
}
JUnit4测试类CalculatorTest
package com.itlwc;
import static org.junit.Assert.*;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
public class CalculatorTest {
private Calculator c = null;
// 初始化方法(每测试一个方法先执行setUp方法)
@Before
public void setUp() throws Exception {
c = new Calculator();
}
@Test
public void testAddition() {
// 真实的值
int actual = c.addition(5, 3);
// 期望的值
int expected = 8;
assertEquals(expected, actual);
}
// 释放方法(每测试一个方法最后都要执行tearDown方法)
@After
public void tearDown() {
c = null;
}
}
测试私有方法使用反射
package com.itlwc;
import static org.junit.Assert.*;
import java.lang.reflect.Method;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
public class CalculatorTest {
private Calculator c = null;
@Before
public void setUp() throws Exception {
c = new Calculator();
}
@Test
public void testSubtraction() {
Class clazz = c.getClass();
try {
Method method = clazz.getDeclaredMethod("addition", new Class[] {
Integer.TYPE, Integer.TYPE });
// 压制编译器的访问限制
method.setAccessible(true);
// 真实的值
Object actual = method.invoke(c, new Object[] { 1, 2 });
// 期望的值
int expected = 4;
assertEquals(expected, actual);
} catch (Exception e) {
fail();
}
}
@After
public void tearDown() {
c = null;
}
}
JUnit3组合测试使用测试套件TestSuite
package test;
import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;
import com.itlwc.CalculatorTest;
public class TestAll extends TestCase {
// 此方法是固定写法
public static Test suite() {
TestSuite suite = new TestSuite();
suite.addTestSuite(CalculatorTest.class);
return suite;
}
}
JUnit3测试CalculatorTest中的testAddition方法20次RepeatedTest
package test;
import junit.extensions.RepeatedTest;
import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;
import com.itlwc.CalculatorTest;
public class TestAll extends TestCase {
// 此方法是固定写法
public static Test suite() {
TestSuite suite = new TestSuite();
/*
执行20次CalculatorTest类中的testAddition()
需要给CalculatorTest增加一个构造器
public CalculatorTest(String name) {
super(name);
}
*/
CalculatorTest c = new CalculatorTest("testAddition");
Test t = new RepeatedTest(c, 20);
suite.addTest(t);
return suite;
}
}
JUnit4组合测试使用注解
package test;
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
import com.itlwc.CalculatorTest;
/*
如果想要同时运行多个测试,需要使用
@RunWith(Suite.class)和@Suite.SuiteClasses()
*/
@RunWith(Suite.class)
@Suite.SuiteClasses( { CalculatorTest.class, CalculatorTest.class })
public class TestAll {
}
JUnit4参数化测试Parameters
package com.itlwc;
import static org.junit.Assert.*;
import java.util.Arrays;
import java.util.Collection;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
/*
当一个测试类使用参数化运行器时,需要在类的声明处加上@RunWith(Parameterized.class)
表示该类不使用JUnit内建的运行器运行,而使用参数化运行器运行
在参数化运行类中提供参数的方式需要使用@Parameters注解来修饰
同时在测试类的构造方法中为参数赋值(构造器是由JUnit调用的)
最后编写测试类,它会根据参数的组数来运行多少次
*/
@RunWith(Parameterized.class)
public class CalculatorTest {
private int expected;
private int input1;
private int input2;
private Calculator c = null;
// 准备数据
@Parameters
public static Collection prepareData() {
Object[][] object = { { 2, 1, 1 }, { 4, 2, 2 }, { 8, 4, 4 } };
return Arrays.asList(object);
}
// 关联属性与准备数据的关系
public CalculatorTest(int index1, int index2, int index3) {
this.expected = index1;
this.input1 = index2;
this.input2 = index3;
}
@Before
public void setUp() throws Exception {
c = new Calculator();
}
@Test
public void testAddition() {
// 期望的值
int actual = c.addition(input1, input2);
assertEquals(expected, actual);
}
@After
public void tearDown() {
c = null;
}
}