Junit4
Unit 是JAVA语言事实上的标准测试库。JUnit 4是三年以来最具里程碑意义的一次发布。它的新特性主要是针对JAVA5中的标记(annotation)来简化测试,而不是利用子类、反射或命名机制。本文将讲述如何使用JUnit 4,当前前提是你最好具有JUnit的使用经验.
JUnit, 由Kent Beck 和 Erich Gamma开发,几乎是JAVA开发最重要的第三方工具。正如Martin Fowler 所说,“在软件开发领域,从来就没有如此少的代码起到了如此重要的作用“。由于JUnit,JAVA代码变得更健壮,更可靠,BUG也比以前更少。由于JUnit (由Smalltalk's的SUnit得来) 的出现,随后产生了许多xUnit的测试工具,如nUnit (.NET), pyUnit (Python), CppUnit (C++), dUnit (Delphi) 和其它不同平台及语言的测试相关的工具。
虽然JUnit也只是一个工具,但其产生的思想和技术却较其架构更意义重大。单元测试,测试先行的编程方式,测试驱动的开发方式,并非必须由JUNIT实现,也不一定要用SWing实现GUI界面。JUNIT最近的一次更新是在三年前,但它比其它大多数有BUG的框架都要健壮,更重要的是,JAVA一直在改进。现在JAVA支持泛型,枚举,可变长度参数,以及标记语言(开创了开发可重用框架的新局面)。
JUnit's的停滞不前使得那些想要变革的开发人员换其它测试工具.挑战者有Bill Venners的Artima SuiteRunner和Cedric Beust的TestNG.这些工具库虽然有值得推荐的功能,但没有任何一款的地位能与JUNIT相比,没有任何一款工具被其它业界产品如Ant, Maven, Eclipse广泛支持.因此Beck 和Gamma双开始利用JAVA5的新特性来开发新版的JUNIT,目的是利用JAVA5中的标记特性使得单元测试开发更容易。Beck说:“JUNIT4的主要目的是通过简化JUNIT的使用鼓励更多的开发人员写更多的测试”。虽然会与以前的版本兼容,但JUNIT4与从JUNIT1.0就开始的版本相比会有一个非常大的变化.
注意: 修改基本框架是一把双刃剑,虽然JUNIT4的目的是清晰的,但细节仍有许多不同,因此本文只是一个简单的介绍,并不是最终文档.
测试方法
以前所有版本的JUNIT都使用命名机制和反射来定位测试,下面的代码测试1+1= 2:
import junit.framework.TestCase; public class AdditionTest extends TestCase { private int x = 1; private int y = 1; public void testAddition() { int z = x + y; assertEquals(2, z); } }
而在JUNIT 4中,测试方法由 @Test 标记说明,如下:
import org.junit.Test; import junit.framework.TestCase; public class AdditionTest extends TestCase { private int x = 1; private int y = 1; @Test public void testAddition() { int z = x + y; assertEquals(2, z); } }
使用标记的好处是你不用将所有测试方法命名为 testFoo() , testBar()等等以"test\"开头的方法,下面的方法也同样可以工作: import org.junit.Test; import junit.framework.TestCase; public class AdditionTest extends TestCase { private int x = 1; private int y = 1; @Test public void additionTest() { int z = x + y; assertEquals(2, z); } }
下面的代码也同样正确:
import org.junit.Test; import junit.framework.TestCase; public class AdditionTest extends TestCase { private int x = 1; private int y = 1; @Test public void addition() { int z = x + y; assertEquals(2, z); } }
这种命名机制最大的优点是更适合你的待测试类或方法名称,例如,你可以使用ListTEst.contains()测试 List.contains() ;使用ListTest.addAll()测试 List.add()等等.
TestCase 还可以继续使用,但你没有必须再扩展为子类,只要你声明了@Test,你可以将测试方法放在任何类中,当然如要访问assert等方法,你必须要引用junit.Assert类,如下:
import org.junit.Assert; public class AdditionTest { private int x = 1; private int y = 1; @Test public void addition() { int z = x + y; Assert.assertEquals(2, z); } }
你也可以使用JDK5中的新特性(static import)使得跟以前版本一样简单:
import static org.junit.Assert.assertEquals; public class AdditionTest { private int x = 1; private int y = 1; @Test public void addition() { int z = x + y; assertEquals(2, z); } }
这种方法测试受保护的方法非常容易,因为你可以在测试类中继承有受保护方法的类.
SetUp 和TearDown
JUnit 3 中 test runners 会在每个测试之前自动调用 setUp()方法。此方法主要用于初始化变量,打开日志,重置环境变量等。下面是XOM's XSLTransformTest中的 setUp()方法:
protected void setUp() { System.setErr(new PrintStream(new ByteArrayOutputStream())); inputDir = new File("data"); inputDir = new File(inputDir, "xslt"); inputDir = new File(inputDir, "input"); }
在JUnit 4中,你仍然可以在每个测试前初始化变量和配置环境,,然而,这些操作可以不用在Setup()中完成,你可以在初始化方法前面添加 @Beforer 来表示,如下:
@Before protected void initialize() { System.setErr(new PrintStream(new ByteArrayOutputStream())); inputDir = new File("data"); inputDir = new File(inputDir, "xslt"); inputDir = new File(inputDir, "input"); }
你也可以有多个方法标记有@Before,所有方法都会在每个测试之前执行:
@Before protected void findTestDataDirectory() { inputDir = new File("data"); inputDir = new File(inputDir, "xslt"); inputDir = new File(inputDir, "input"); } @Before protected void redirectStderr() { System.setErr(new PrintStream(new ByteArrayOutputStream())); }
清除环境与JUNIT3 差不多,在JUNIT3中使用 tearDown()方法,下面的代码是结束测试时回收内存:
protected void tearDown() { doc = null; System.gc(); }
在JUnit 4中,你还可以使用 @After 标记来说明:
@After protected void disposeDocument() { doc = null; System.gc(); }
与 @Before一样,你也可以有多个标记有 @After的清除方法,每个都会在执行完每个测试后执行。
最后,你不需要在父类中明确调用这些初始化或清除方法.test runner会自动调用这些标记的方法.子类中的@Before方法在父类的@Before方法之后执行(这与构造函数的执行顺序一样),而@After方法刚好相反,子类中的@After方法先执行.然而,多个@Before和@After方法的执行顺序就是未知的.
写一个整数除法和乘法的类,并且给他写测试用例:
1) 建立Math类
工具是eclipse3.3
Java代码
/**
* @author bulargy.j.bai
* @创建时间:Mar 10, 2008
* @描述:一个整数除法和乘法的工具类
*/
public class Math {
public static int divide(int x,int y) {
return x/y;
}
public static int multiple(int x,int y) {
return x*y;
}
}
/**
* @author bulargy.j.bai
* @创建时间:Mar 10, 2008
* @描述:一个整数除法和乘法的工具类
*/
public class Math {
public static int divide(int x,int y) {
return x/y;
}
public static int multiple(int x,int y) {
return x*y;
}
}
2) 建立测试用例
选中需要建立测试用例的包,选择new->other->JUnit Test Case。
有5个方法可以选择:
setUp()方法在测试方法前调用,一般用来做测试准备工作。
tearDown()方法在测试方法后调用,一般作测试的清理工作。
setUpBeforeClass()方法在整个类初始化之后调用,一般用来做测试准备工作。
tearDownAfterClass()方法在整个类结束之前调用,一般作测试的清理工作。
constructor()为是否包含构造方法。
自动生成的代码如下:
Java代码
/**
* @author bulargy.j.bai
* @创建时间:Mar 11, 2008
* @描述:
*/
public class MathTest {
@BeforeClass
public static void setUpBeforeClass() throws Exception {
}
@AfterClass
public static void tearDownAfterClass() throws Exception {
}
@Test
public void testDivide() {
fail("Not yet implemented");
}
@Test
public void testMultiple() {
fail("Not yet implemented");
}
}
/**
* @author bulargy.j.bai
* @创建时间:Mar 11, 2008
* @描述:
*/
public class MathTest {
@BeforeClass
public static void setUpBeforeClass() throws Exception {
}
@AfterClass
public static void tearDownAfterClass() throws Exception {
}
@Test
public void testDivide() {
fail("Not yet implemented");
}
@Test
public void testMultiple() {
fail("Not yet implemented");
}
}
说明:
@BeforeClass标签注释的方法用于在整个类测试过程的初始化后调用一次,@AfterClass标签注释的方法则是整个测试类结束之前调用一次。这2个标签的搭配可以避免使用@Before、@After标签组合在每个测试方法前后都调用的弊端,减少系统开销,提高系统测试速度。(不过对环境独立性要求较高的测试还是应当使用@Before、@After来完成)
@Test标签用来标注待测试的方法,按照类中声明的顺序执行。
我们在testDivide方法加入测试代码,分别测试三种情况:
a. 完全正确也没有可能出错的数据,如:9除3 结果必须等于3
b. 可能有问题的边缘数据,如:10除3 结果也必须等于3
c. 错误的数据,如:10除0 必须抛出异常
忽略testMultiple方法
代码如下:
Java代码
@Test(expected=ArithmeticException.class)
public void testDivide() {
assertEquals(3,Math.divide(9,3));
assertEquals(3,Math.divide(10,3));
Math.divide(10,0); //除数不能为0,会抛出异常
}
@Ignore("忽略乘法测试")
@Test
public void testMultiple() {
}
@Test(expected=ArithmeticException.class)
public void testDivide() {
assertEquals(3,Math.divide(9,3));
assertEquals(3,Math.divide(10,3));
Math.divide(10,0); //除数不能为0,会抛出异常
}
@Ignore("忽略乘法测试")
@Test
public void testMultiple() {
}
说明:
Junit4为测试方法增加了判断异常的方式,避免了以前还要通过try/catch块捕捉异常再抛出的复杂方式,简单的这样声明“@Test(expected=ArithmeticException.class)”Junit4就会检查此方法是否抛出ArithmeticException异常,如果抛出则测试通过,没抛出则测试不通过(@Test标签还有一些其他参数,例如超时测试@Test(timeout=1)这样,但是由于并不能准确反应实际时间,所以应用较少,经过我测试误差太大绝对不适合拿来做超时测试的)
@Ignore标签会告诉Junit4忽略它所标注的方法,例如数据库不可用时可以用此标注标注一些测试数据库连接的方法来避免测试失败。
github链接:https://github.com/hanzhaoyan/Junit4-/tree/master