java|Junit4精简解析

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()); } }

【java|Junit4精简解析】为了简化类似的测试,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 allSort2(String str, int pos) { ArrayList list = new ArrayList(); 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 publicvoid 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")); }}

    推荐阅读