JUnit官网对JUnit的简单解释:java
JUnit is a simple framework to write repeatable tests. It is an instance of the xUnit architecture for unit testing frameworks.设计模式
JUnit是一个简单的可复用的测试框架,是xUnit测试框架的一个子集。数组
xUnit是一套基于测试驱动开发的测试框架。框架
xUnit包括:PythonUnit、CppUnit等等。JUnit应该算是xUnit家族最成功的一个了。单元测试
使用JUnit可以帮助咱们减小在开发过程当中的错误,把Bug扼杀在萌芽之中,有利于代码的后期维护和检查。作好了单元测试能够缩短开发周期,提升代码质量。这样咱们就能够把更多的时间用到咱们应该干的事情上来而不是去解决项目后期发现的愈来愈多的使人头疼的问题。测试
测试不是用来证实你是对的,而是用来证实你没有错。ui
JUnit官网:http://junit.org/this
首先访问JUnit官网,找到welcome模块:spa
而后点击下载相应版本的JUnit就OK了。设计
准守必定的规则会使咱们的项目和代码看起来更加的和谐。
通常来讲规则是这样的:
a. 在Java项目(这里为JUnitTest)中建立一个名为test的source folder。
b. 在test目录中建立和src目录下相同的包名,在其中存放对应的测试类。
c. 不要忘了引入JUnit及其依赖jar包。
等项目完成以后把test测试源码文件删除便可,不影响其余的代码。
项目的结构目录看起来应该是这样的:
这样咱们就能够来进行测试代码的编写了。
a.首先在src/util包下建立Calculator.java,编写一个简单的加法运算器。
package util; /** *被测试的类 *@author wxisme *@time 2015-9-2 上午9:52:24 */ public class Calculator { public int add(int a, int b) { return a + b; } }
b. 在test/util包下建立CalculatorTest.java做为Calculator的测试类。
package util; import junit.framework.Assert; import org.junit.Before; import org.junit.Test; import util.Calculator; /** * *@author wxisme *@time 2015-9-1 下午8:44:15 */ public class CalculatorTest { private static Calculator calculator; @BeforeClass public static void BuildCalculator() { calculator = new Calculator(); } @Test public void testAdd() { Assert.assertEquals(8, calculator.add(3, 5)); } }
上面的程序JUnit4提供的注解和方法。
首先看@Test注解,见名知意,是标注testAdd()方法为一个测试方法。
Assert.assertEquals(8, calculator.add(3, 5));方法是JUnit中Assert类提供的断言方法。可以判断咱们的预期结果和程序的输出结果是不是一致的。
咱们来测试一下:选中testAdd()方法,右键选择run as JUnit test:
上面程序运行的结果应该是这样的:
看到有绿色的条,而且Error和Failure的值都为零,说明咱们的测试经过。程序运行结果与咱们所指望的是一致的。
那若是把testAdd()方法中的断言改为:
Assert.assertEquals(8, calculator.add(3, 5));
运行结果会是怎样的呢?
应该是这样的:

从红色圈起来的地方咱们能够得知,测试没有经过,而且错误在于,咱们指望的值为7,可是程序的输出结果为8.
c. 经过上面的例子咱们能够有一下总结:
测试方法必须使用@Test修饰
测试方法必须使用public void修饰
新建test源代码目录来存放测试代码
测试类的包名应该和被测试类的报名保持一致
测试单元中的每一个方法必须能够独立测试,测试方法之间不能有任何依赖
测试类通常使用Test做为后缀
测试方法通常使用test做为前缀
JUnit4中提供的经常使用注解大体包括:
@BeforeClass
@AfterClass
@Before
@After
@Test
@Ignore
@RunWith
咱们经过一个例子来了解他们的用法:
编写一个MethodTest来测试注解的用法。
package util; import org.junit.After; import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; /** * *@author wxisme *@time 2015-9-2 上午11:28:29 */ public class MethodTest { @BeforeClass //用static修饰 public static void setUpBeforeClass() throws Exception { System.out.println("beforeClass..."); } @AfterClass //用static修饰 public static void tearDownAfterClass() throws Exception { System.out.println("afterClass..."); } @Before public void setUp() throws Exception { System.out.println("before..."); } @After public void tearDown() throws Exception { System.out.println("after..."); } @Test public void test1() { System.out.println("test1..."); // fail("Not yet implemented"); } @Test public void test2() { System.out.println("test2..."); // fail("Not yet implemented"); } }
测序的运行后再Console的中输出以下:
beforeClass...
before...
test1...
after...
before...
test2...
after...
afterClass...
其中咱们能够总结出这几个注解的执行顺序和做用:
@Test将一个普通的方法修饰为一个测试方法
@BeforeClass会在全部方法运行前执行,static修饰
@AfterClass会在全部方法运行结束后执行,static修饰
@Before会在每一个测试方法运行前执行一次
@After会在每一个测试方法运行后被执行一次
这就是为何在第一个例子中我会这么干了(全部的测试方法只使用一个Calculator就好了):
private Calculator calculator; @BeforeClass public static void BuildCalculator() { calculator = new Calculator(); }
@Test注解还能够带有参数。像这样:
@Test(expected=xx.class)
@Test(timeout=毫秒)
timeout参数很容易理解,就是指定程序运行的超时时间,单位为毫秒。
咱们来看一下expected参数。
看一个例子:
在第一个例子里面加一个Exception异常属性,在testAdd()方法中抛出。
package util; import junit.framework.Assert; import org.junit.Before; import org.junit.Test; import util.Calculator; /** * *@author wxisme *@time 2015-9-1 下午8:44:15 */ public class CalculatorTest { private static Calculator calculator; private static Exception ex; @BeforeClass public static void BuildCalculator() { calculator = new Calculator(); ex = new Exception("手动抛出的异常!"); } @Test public void testAdd() throws Exception { Assert.assertEquals(8, calculator.add(3, 5)); throw ex; } }
结果会是这样的:
发现测试没有经过,而且抛出了一个异常,使咱们手动抛出的异常。
那么把上面的程序总@Test注解加上这样的参数:
@Test(expected=Exception.class)
再看结果:
发现测试经过了。
是否是忽然就明白expected参数的做用了呢?
其余几个注解的功能能够类比。
再来看@Ignore和@RunWith
@Ignore见名知意,做用是让测试运行器忽略其修饰的测试方法。@Ignore所修饰的方法不会执行。
自行测试。
@RunWith能够用来更改运行测试器org.junit.runner.Runner。
下面的两个例子都是经过@RunWith注解来实现的。
试想若是项目中有100个测试类须要测试,你会怎么作呢?每一个类运行一次?运行100次?
JUnit固然不会只容许有这一个愚蠢的方法。答案就是测试套件。
测试套件用来组织批量运行测试类。看一个例子。
首先来建立3个测试类模拟须要批量运行的测试类群。
public class DemoTest1 { @Test public void test() { System.out.println("DemoTest1..."); } } public class DemoTest2 { @Test public void test() { System.out.println("DemoTest2..."); } } public class DemoTest3 { @Test public void test() { System.out.println("DemoTest3..."); } }
而后建立一个名为SuiteTest的测试类,并加上@RunWith注解和@Suite.SuiteClasses注解。(该类中没有其余测试方法)
package util; import org.junit.runner.RunWith; import org.junit.runners.Suite; /** *测试套件 *@author wxisme *@time 2015-9-2 下午7:37:56 */ @RunWith(Suite.class) @Suite.SuiteClasses({DemoTest1.class,DemoTest2.class,DemoTest3.class}) public class SuiteTest { }
测试程序总首先用@RunWith来修改测试运行器。
而后使用@Suite.SuiteClasses来指定要批量执行的测试类(数组的形式)
程序的运行结果应该是这样的(经过测试而且在Console中输出):
可见咱们的批量运行生效了。
再试想一个场景:若是一个测试方法中须要测试100组数据是否和指望值一致,你会怎么作呢?手动copy+change100次?
JUnit也不会只容许使用这么愚蠢的方法。这就用到了JUnit的参数化设置。
看一个例子:
a.首先建立一个名为ParameterTest的测试类,并用RunWith注解来改变测试运行器。
b.声明变量来存放预期值和结果值。
c.声明一个返回值为Collection的公共静态方法,并使用 @Parameters进行修饰。
d.为测试类声明一个带有参数的公共构造方法,并在其中为之声明变量赋值。
代码应该是这样的:
package util; import java.util.Arrays; import java.util.Collection; import junit.framework.Assert; import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameters; /** *JUnit参数化设置 *@author wxisme *@time 2015-9-2 下午8:03:47 */ @RunWith(Parameterized.class) public class ParameterTest { private static Calculator calculator; @BeforeClass public static void setUpBeforeClass() { calculator = new Calculator(); } private int expected; private int input1; private int input2; @Parameters public static Collection<Object[]> setParameters() { Object[][] objs = {{8,3,5},{5,3,2}}; return Arrays.asList(objs); } public ParameterTest(int expected, int input1, int input2) { this.expected = expected; this.input1 = input1; this.input2 = input2; } @Test public void testParameters() { Assert.assertEquals(expected, calculator.add(input1, input2)); } }
而后咱们运行这个测试类(要在类的级别上运行,若是在方法上运行就会有初始化错误)。
结果应该是这样的:
运行结果显示两组测试数据均测试经过。
到此JUnit的基本用法就介绍完了,关于JUnit的其余用法以及断言的API请参考官方提供的document(若是你不想本身找的话,在评论区留下邮箱,我会发给你的哦)。
JUnit带给咱们的不只是开发效率、代码质量的提升,更是一种思想的提升,如今都在讲测试驱动开发、回归质量大概就是这种思想。
JUnit使用起来不只简单方便,其源码更是短小精悍,Erich Gamma 是著名的 GoF 之一,在JUnit中设计模式的使用堪称经典,有优良的扩展性和可重用性。值得细细品味。
敬请关注将要发表的《JUnit源码解析》系列博客(这须要提早了解Java的注解、反射等高级特性)。
@Copyright:http://www.cnblogs.com/wxisme/ 不当之处,欢迎交流。