JUnit4是JUnit框架有史以来的最大改进,其主要目标便是利用Java5的Annotation特性简化测试用例的编写。先简单解释一下什么是Annotation,这个单词一般是翻译成元数据。元数据是什么?元数据就是描述数据的数据。也就是说,这个东西在Java里面可以用来和public、static等关键字一样来修饰类名、方法名、变量名。修饰的作用描述这个数据是做什么用的,差不多和public描述这个数据是公有的一样。想具体了解可以看Core Java2。
废话不多说了,直接进入正题。
比如你在一个叫AddOperation的类中定义一个计算加法的方法
public class AddOperation {
public int add(int x,int y){
return x+y;
}
}
那么我们要测试这个加的方法就可以在同一包下新建一个Junit Test Case文件,里面的重载方法默认就行了。
import junit.framework.TestCase;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import static org.junit.Assert.*;
/**
*
* @author bean
*/
public class AddOperationTest extends TestCase{
public AddOperationTest() {
}
@BeforeClass
public static void setUpBeforeClass() throws Exception {
}
@AfterClass
public static void tearDownAfterClass() throws Exception {
}
@Before
public void setUp() throws Exception {
}
@After
public void tearDown() throws Exception {
}
@Test
public void add() {
System.out.println("add");
int x = 0;
int y = 0;
AddOperation instance = newAddOperation();
int expResult = 0;
int result = instance.add(x,y);
assertEquals(expResult,result);
}
}
从上面的例子可以看到在JUnit 4中还引入了一些其他的元数据,下面一一介绍:
@BeforeClass
在测试类开始运行时调用这个Annotaion里的方法
@AfterClass
在整个测试类结束时调用这个Annotation里的方法
@Before:
使用了该元数据的方法在每个测试方法执行之前都要执行一次。
某些方法在每次执行前需要一些初始化工作的话,使用@Before做准备工作,然后再测试。
@After:
使用了该元数据的方法在每个测试方法执行之后要执行一次。
进行一些资源释放和内存清理。
注意:@Before和@After标示的方法只能各有一个。这个相当于取代了JUnit以前版本中的setUp和tearDown方法,当然你还可以继续叫这个名字,不过JUnit不会霸道的要求你这么做了。
@Test(expected=*.class)
在JUnit4.0之前,对错误的测试,我们只能通过fail来产生一个错误,并在try块里面assertTrue(true)来测试。现在,通过@Test元数据中的expected属性。expected属性的值是一个异常的类型
@Test(timeout=xxx):
该元数据传入了一个时间(毫秒)给测试方法,
如果测试方法在制定的时间之内没有运行完,则测试也失败。
@ignore:
该元数据标记的测试方法在测试中会被忽略。当测试的方法还没有实现,或者测试的方法已经过时,或者在某种条件下才能测试该方法(比如需要一个数据库联接,而在本地测试的时候,数据库并没有连接),那么使用该标签来标示这个方法。同时,你可以为该标签传递一个String的参数,来表明为什么会忽略这个测试方法。比如:@lgnore(“该方法还没有实现”),在执行的时候,仅会报告该方法没有实现,而不会运行测试方法。
@Test(expected = ArithmeticException.class)
异常测试,比如指定可能的一场为算术异常。
@Test(expected = ArithmeticException.class)
public void divideByZero() ...{
calculator.divide(0);
}
介绍完简单的测试之后,再来说下复杂一点的参数测试。
参数化测试。
你可能遇到过这样的函数,它的参数有许多特殊值,或者说他的参数分为很多个区域。比如,一个对考试分数进行评价的函数,返回值分别为“优秀,良好,一般,及格,不及格”,因此你在编写测试的时候,至少要写5个测试,把这5中情况都包含了,这确实是一件很麻烦的事情。我们还使用我们先前的例子,测试一下“计算一个数的平方”这个函数,暂且分三类:正数、0、负数。测试代码如下:
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import static org.junit.Assert.*;
public class AdvancedTest ...{
private static Calculator calculator = new Calculator();
@Before
public void clearCalculator() ...{
calculator.clear();
}
@Test
public void square1() ...{
calculator.square(2);
assertEquals(4, calculator.getResult());
}
@Test
public void square2() ...{
calculator.square(0);
assertEquals(0, calculator.getResult());
}
@Test
public void square3() ...{
calculator.square(-3);
assertEquals(9, calculator.getResult());
}
}
为了简化类似的测试,JUnit4提出了“参数化测试”的概念,只写一个测试函数,把这若干种情况作为参数传递进去,一次性的完成测试。代码如下:
import static org.junit.Assert.assertEquals;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
import java.util.Arrays;
import java.util.Collection;
@RunWith(Parameterized.class)
public class SquareTest ...{
private static Calculator calculator = new Calculator();
private int param;
private int result;
@Parameters
public static Collection data() ...{
return Arrays.asList(new Object[][]...{
...{2, 4},
...{0, 0},
...{-3, 9},
});
}
//构造函数,对变量进行初始化
public SquareTest(int param, int result) ...{
this.param = param;
this.result = result;
}
@Test
public void square() ...{
calculator.square(param);
assertEquals(result, calculator.getResult());
}
}
下面我们对上述代码进行分析。首先,你要为这种测试专门生成一个新的类,而不能与其他测试共用同一个类,此例中我们定义了一个SquareTest类。然后,你要为这个类指定一个Runner,而不能使用默认的Runner了,因为特殊的功能要用特殊的Runner嘛。@RunWith(Parameterized.class)这条语句就是为这个类指定了一个ParameterizedRunner。第二步,定义一个待测试的类,并且定义两个变量,一个用于存放参数,一个用于存放期待的结果。接下来,定义测试数据的集合,也就是上述的data()方法,该方法可以任意命名,但是必须使用@Parameters标注进行修饰。这个方法的框架就不予解释了,大家只需要注意其中的数据,是一个二维数组,数据两两一组,每组中的这两个数据,一个是参数,一个是你预期的结果。比如我们的第一组{2, 4},2就是参数,4就是预期的结果。这两个数据的顺序无所谓,谁前谁后都可以。之后是构造函数,其功能就是对先前定义的两个参数进行初始化。在这里你可要注意一下参数的顺序了,要和上面的数据集合的顺序保持一致。如果前面的顺序是{参数,期待的结果},那么你构造函数的顺序也要是“构造函数(参数, 期待的结果)”,反之亦然。最后就是写一个简单的测试例了,和前面介绍过的写法完全一样,在此就不多说。
最后上两个完整的代码片(talk is cheap show me the code)
参数测试
Math类
package perMute;
import TreeSet.intCompare;
public class Math {
public Math(){
}
public int add(int i,int j) {
return i+j;
}
}
MathTest类(参数化测试)
package perMute;
import java.util.Arrays;
import java.util.Collection;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
/**
* 参数化测试的类必须有Parameterized测试运行器修饰
*
*/
@RunWith(Parameterized.class)
public class mMathTest {
Math math = new Math();
private int input1;
private int input2;
private int expected;
/**
* 准备数据。数据的准备需要在一个方法中进行,该方法需要满足一定的要求:
*
* 1)该方法必须由Parameters注解修饰 2)该方法必须为public static的 3)该方法必须返回Collection类型
* 4)该方法的名字不做要求 5)该方法没有参数
*
* @return
*/
@Parameters
@SuppressWarnings("unchecked")
public static Collection prepareData() {
Object[][] object = { { -1, -2, -3 }, { 0, 2, 2 }, { -1, 1, 0 },
{ 1, 2, 3 } };
return Arrays.asList(object);
}
public mMathTest(int input1, int input2, int expected) {
this.input1 = input1;
this.input2 = input2;
this.expected = expected;
}
@Test
public void testAdd() {
Assert.assertEquals(expected, math.add(input1, input2));
}
}
快速排序和全排列单元测试
待测试类(三个方法需要被测试)
package perMute;
import java.awt.List;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
public class perMute {
public String[] allSort1(String str) {
int len = str.length();
if (len == 0) {
return null;
}
int count = 0, n = 1;
for (int i = 1; i <= len; i++) {
n *= i;
}
String[] strings = new String[n];
String mtuxString = str;
if (len == 1) {
strings[0] = mtuxString;
}
// while (!str.equals(mtuxString)) {
// mtuxString = swap(mtuxString, n, --n);
// }
for (int i = 0; i < n / len; i++) {
if (i % 2 == 0) {
for (int j = len - 1; j > 0; j--) {
mtuxString = swap(mtuxString, j, j - 1);
strings[count++] = mtuxString;
if (j == 1) {
mtuxString = swap(mtuxString, len - 1, len - 2);
strings[count++] = mtuxString;
}
}
} else {
for (int j = 1; j <= len - 1; j++) {
mtuxString = swap(mtuxString, j, j - 1);
strings[count++] = mtuxString;
if (j == len - 1) {
mtuxString = swap(mtuxString, 0, 1);
strings[count++] = mtuxString;
}
}
}
}
return strings;
}
public static ArrayList<String> allSort2(String str, int pos) {
ArrayList<String> list = new ArrayList<String>();
if(pos==0){
// str=quickSort(str);
}
int len = str.length();
if (len == 0) {
list.add("");
return list;
}
list.add(str);
if (len == 1) {
return list;
}
for (int i = pos; i < len; i++) {
str = swap(str, i, pos);
list.add(str);
if (pos < len - 1) {
list.addAll(allSort2(str, pos + 1));
} else {
return list;
}
}
return list;
}
public static String swap(String str, int i, int j) {
if (str.length() == 0) {
return null;
}
String afterSwap = "";
char c;
char[] cs = str.toCharArray();
c = cs[i];
cs[i] = cs[j];
cs[j] = c;
afterSwap = String.valueOf(cs);
return afterSwap;
}
/**
* Qucik sort 54,12,9,89,3,19,29,1 (1)选择54作为中轴 并定义tmp=54 1,12,9,89,3,19,29,1
* (2) 定义一个队尾游标,从后向前扫描,选择小于54的第一个元素,并把它复制到54这个位置 1,12,9,89,3,19,29,89
* (3)定义一个队首游标,从前向后扫描,扫描到大于54的第一个元素,并把它复制到1这个位置 1,12,9,29,3,19,29,89
* (4)继续步骤2的扫描,扫描小于54的第一个元素,并把它复制到89这个位置 1,12,9,29,3,19,54,89
* (5)继续步骤3的扫描,当扫描游标触碰到队尾游标时还未发现大于54的元素,则把tmp复制到队尾游标所指向的位置
* (1,12,9,29,3,19)54(89) (6)分别对54左右两个数组执行步骤1
*
* @param str
* @return
*/
public static String quickSort(String str) {
int len = str.length();
if (len == 0) {
return "";
}
if (len == 1) {
return str;
}
String resultStr = "";
char[] cr = str.toCharArray();
char pivol = cr[0];
int i = 0, j = len - 1;
while (true) {
if (i < j) {
while (j >= 0 && j > i) {
if (cr[j] > pivol) {
j--;
} else {
cr[i] = cr[j];
break;
}
}
while (i < len && j > i) {
if (cr[i] <= pivol) {
i++;
} else {
cr[j] = cr[i];
break;
}
}
} else {
cr[j] = pivol;
resultStr = String.valueOf(cr);
break;
}
}
return quickSort(resultStr.substring(0, j)) + pivol
+ quickSort(resultStr.substring(j + 1, len));
}
public static void cout1() {
System.out.println("class before");
}
public static void cout2() {
System.out.println("method before");
}
public static void cout3() {
System.out.println("class after");
}
public static void cout4() {
System.out.println("method after");
}
测试类
package perMute;
import static org.junit.Assert.*;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test;
public class perMuteTest {
perMute pm = new perMute();
@BeforeClass
public static void testBeforeClass() {
// TODO Auto-generated method stub
perMute.cout1();
}
@Before
public void testbefore() {
// TODO Auto-generated method stub
perMute.cout2();
}
@AfterClass
public static void testAfterClass(){
perMute.cout3();
}
@After
public void testAfter(){
perMute.cout4();
}
@Test
public void testAllSort1() {
assertNull(pm.allSort1(""));
String[] strings1 = { "a" };
assertArrayEquals(strings1, pm.allSort1("a"));
String[] strings2 = { "ba", "ab" };
assertArrayEquals(strings2, pm.allSort1("ab"));
String[] strings3 = { "acb", "cab", "cba", "bca", "bac", "abc" };
assertArrayEquals(strings3, pm.allSort1("abc"));
String[] strings4 = { "abdc", "adbc", "dabc", "dacb", "adcb", "acdb",
"acbd", "cabd", "cadb", "cdab", "dcab", "dcba", "cdba", "cbda",
"cbad", "bcad", "bcda", "bdca", "dbca", "dbac", "bdac", "badc",
"bacd", "abcd" };
assertArrayEquals(strings4, pm.allSort1("abcd"));
};
@Ignore("UnImplemented do not test")
@Test
public void testAllSort2() {
fail("Not yet implemented");
}
@Test(timeout=10)
public void testSwap() {
assertNull("null", pm.swap("", 0, 0));
assertNull("null", pm.swap("", 0, 2));
assertEquals(pm.swap("a", 0, 0), "a");
assertEquals(pm.swap("a", 0, 0), "a");
assertEquals(pm.swap("abc", 1, 2), "acb");
assertEquals(pm.swap("abcd", 2, 3), "abdc");
assertEquals(pm.swap("abcd", 0, 1), "bacd");
}
@Test(timeout=50)
public void testquickSort(){
assertEquals("", pm.quickSort(""));
assertEquals("a", pm.quickSort("a"));
assertEquals("1", pm.quickSort("1"));
assertEquals("22", pm.quickSort("22"));
assertEquals("123", pm.quickSort("123"));
assertEquals("123", pm.quickSort("321"));
assertEquals("123", pm.quickSort("213"));
assertEquals("123", pm.quickSort("312"));
assertEquals("1223", pm.quickSort("3212"));
assertEquals("123456", pm.quickSort("123456"));
assertEquals("123456", pm.quickSort("653214"));
assertEquals("abc", pm.quickSort("abc"));
assertEquals("abc", pm.quickSort("bca"));
assertEquals("abc", pm.quickSort("cab"));
assertEquals("cccc", pm.quickSort("cccc"));
assertEquals("acuyz", pm.quickSort("acuyz"));
assertEquals("acuyz", pm.quickSort("uyzca"));
assertEquals("acuyz", pm.quickSort("czyua"));
}
}