Junit单元测试
来源:互联网 发布:淘宝好看的女鞋店铺 编辑:程序博客网 时间:2024/04/30 15:36
Junit是干什么的我在这里就不讲解了。直接开始正题。
常用的注解
Junit中的注解很多,我们首先来看一下最常用的一些注解
- @Test:把一个方法标记未测试方法
- excepted:用来测试异常的,方法抛出该异常说明测试成功
- timeout:用来测试性能的,在规定的时间内完成,说明成功。注意单位是毫秒
- @Before:每个测试方法执行前自动调用一次
- @After:每个测试方法执行完自动调用一次
- @BeforeClass:在类被加载到内存中后执行,这意味着方法需要时static的,并且在构造函数之前执行
- @AfterClass:在类被销毁之前执行,所以需要时static的
- @Ignore:暂不执行该测试方法
下面我们将使用一个简单的例子,将这些注解贯穿起来,一起进行讲解:
public class JunitTest { private int count = 10; public JunitTest() { System.out.println("构造函数运行"); } @BeforeClass public static void beforeClass(){ System.out.println("BeforeClass..."); } @AfterClass public static void afterClass(){ System.out.println("AfterClass..."); } @Before public void setUp() throws Exception { System.out.println("Before Method run..."); } @After public void tearDown() throws Exception { System.out.println("After Method run..."); } @Test public void test1() throws Exception { count++; System.out.println("Test1 run,count=" + count); } @Test(expected = RuntimeException.class) public void test2() throws Exception { count++; System.out.println("Test2 run, count=" + count); throw new RuntimeException("Exception"); } @Test(timeout = 500) public void test3() throws Exception { count++; System.out.println("Test3 run, count=" + count); Thread.sleep(300); } @Ignore public void test4() throws Exception{ count++; System.out.println("Test4 run, count=" + count); }}
程序的运行结果如下:
BeforeClass...构造函数运行Before Method run...Test1 run,count=11After Method run...构造函数运行Before Method run...Test2 run, count=11After Method run...构造函数运行Before Method run...Test3 run, count=11After Method run...AfterClass...
上述程序中,包含了四个测试用例,但是有一个标记未@Ignore,因此只运行了三个。所以我们可以将上述的运行结果拆分为三个部分进行分析,得出如下的结论:
- BeforeClass在构造函数之前运行,需要是static的。AfterClass在程序的最后运行
- 在运行每个测试用例的时候,都重新构造了类的一个实例,Junit这样做保证了每个测试用例的相互独立性。这也解释了为什么构造函数运行了三次,以及count值始终为11.
- Before和After标记的方法在每个测试用例的前后执行
- 测试用例的运行顺序,其实是没有规定的,也就是test1()方法不一定非得在test2()之前运行。这里只是因为test1(),test2(),test3()他们的方法名恰巧是按字母序递增的,所以才会这样。如果,我们将测试方法test3()名字改为test0(),那么我们将看到test0()将在test1()前面运行。关于测试用例的运行顺序,稍后我将给大家介绍。
- expected属性用来对异常进行测试,如果抛出该异常,那么测试用例通过;timeout用来对性能进行测试,指定一个上限时间,如果超过这个时间,测试方法还没有运行结束,则直接中断该方法。如果该方法正在执行不响应中断的操作,则方法永远不能结束。
Suite
Suite可以翻译为套件,意味着将多个单元测试类一起执行。在Junit中,需要使用@RunWith和@SuiteClasses注解
public class Test1 { @Test public void test1(){ System.out.println("test1"); }}public class Test2 { @Test public void test1(){ System.out.println("test2"); }}public class Test3 { @Test public void test1(){ System.out.println("test3"); }}@RunWith(Suite.class)@Suite.SuiteClasses({ Test1.class, Test2.class, Test3.class})public class FeatureTestSuite { //do nothing, just suite them all}
运行FeatureTestSuite后,测试类将按照在@SuiteClasses中定义的顺序依次执行,结果为
test1test2test3
Parameters
Parameters,参数化测试,允许开发人员使用不同的参数多次运行同一个测试用例。需要@RunWith和@parameters注解配合使用
//计算Fibonacci数public class Fibonacci { public static int compute(int n ){ int result = 0; if(n <= 1){ result = n; }else{ result = compute(n-1) + compute(n-2); } return result; }}@RunWith(Parameterized.class)public class FibonacciTest { @Parameterized.Parameters public static Collection<Object[]> data(){ return Arrays.asList(new Object[][]{ {0, 0},{1, 1},{2, 1},{3, 2},{4, 3} }); } private int fInput; private int fExpected; public FibonacciTest(int fInput, int fExpected) { this.fInput = fInput; this.fExpected = fExpected; } @Test public void compute() throws Exception { assertEquals(fExpected, Fibonacci.compute(fInput)); }}
上述程序将分别使用{0, 0},{1, 1},{2, 1},{3, 2},{4, 3}这五组样例作为输入,运行compute()程序
我们还可以通过@Parameters的name属性,来为每个测试用例命名,例如,我们在data()上这样添加name属性
@Parameterized.Parameters(name = "{index}: fib({0})={1}")
那么,测试类的运行结果为:
Exception Test
前面我们介绍过,通过在@Test上添加excepted属性,可以用来对异常进行测试。但是这样只能测试异常的类型,不能够对异常中的message进行测试。如果我们对异常中抛出的message感兴趣,那么我们可以尝试使用ExpectedException规则(Rule)。
public class ExceptionRule { @Test public void showException() throws Exception{ List<Object> list = new ArrayList<>(); list.get(0);//just for test }}
运行上述这段代码将抛出IndexOutOfBoundsException异常,message为:Index: 0, Size: 0,后面跟上方法的调用堆栈。为了对message进行测试,我们添加一个Rule和一个测试类,并将原来的测试方法设置expected使其通过。添加后的完整代码为:
public class ExceptionRule { @Test(expected = IndexOutOfBoundsException.class) public void showException() throws Exception{ List<Object> list = new ArrayList<>(); list.get(0);//just for test } @Rule public ExpectedException thrown = ExpectedException.none(); @Test public void showTestExceptionMessage() throws Exception { List<Object> list = new ArrayList<>(); thrown.expect(IndexOutOfBoundsException.class); //相当于expected thrown.expectMessage("Index: 0, Size: 0"); //期望消息中包含Index: 0, Size: 0 list.get(0); }}
再次运行上面的测试,发现测试方法均能够通过。上面的程序只是演示了ExpectedException的一种最简单的用法,通过和hamcrest包中的Matcher相结合,我们还能够对消息进行很多有趣的操作。
Timeout
前面已经介绍过,我们可以在@Test上添加timeout属性来对方法进行性能测试,这样的确能够解决问题。考虑这样一种情况:我的测试类中含有一百个测试方法,我需要测试这一百个方法每一个的运行时间均小于一个阈值(threshold),这时我们应该怎么做呢?是为每一个Test上均添加一个timeout属性吗?No,我们可以使用Timeout Rule来解决问题
public class TestTimeout { private final CountDownLatch latch = new CountDownLatch(1); @Rule public Timeout globalTimeout = Timeout.millis(500); @Test public void testSleepForTooShort() throws Exception{ TimeUnit.MILLISECONDS.sleep(100); } @Test public void testSleepForTooLong() throws Exception { TimeUnit.SECONDS.sleep(1); } @Test public void testBlockForever() throws Exception { latch.await(); //waitforever until timeout }}
上面这个测试类中,我们使用CountDownlatch的await操作来模拟永久等待(由于没有countDown操作)。运行上面的程序,只有testSleepForTooShort()运行通过,其他两个方法均抛出异常。这说明Timeout Rule对所有的Test方法均有效,并且超时的时候会通过中断的方式打断Test方法的运行
org.junit.runners.model.TestTimedOutException: test timed out after 500 milliseconds
Test Execution Order(测试用例的运行顺序)
Junit框架在设计的时候并没有考虑测试方法应该按照什么样的顺序执行,默认是按照Java反射API返回的顺序逐个调用。诚然,良好的测试类是不应该预设测试方法的执行顺序的,但是有些场景下,我们确实有必要使得测试方法按照一定的顺序去执行。
从Junit4.11开始,测试方法将按照MethodSorters.DEFAULT排序方法进行运行。为了改变测试方法的运行顺序,我们可以使用@FixMethodOrder注解来进行指定
- @FixMethodOrder(MethodSorters.JVM):让JVM来决定测试方法的运行顺序。这种排序模式下,每一次的运行顺序有可能大不相同
- @FixMethodOrder(MethodSorters.NAME_ASCENDING):按照字典升序对测试方法名进行排序后执行
@FixMethodOrder(MethodSorters.NAME_ASCENDING)public class TestMethodOrder { @Test public void testA() throws Exception { System.out.println("first"); } @Test public void testB() throws Exception { System.out.println("second"); } @Test public void testC() throws Exception { System.out.println("third"); }}
测试方法将按照testA->testB->testC的顺序依次运行
Assert
Junit中提供了很多断言(Assert),其中大部分的断言都很好理解,我们需要注意的是assertThat断言。首先我们来看下assertThat长什么样:
<T> void assertThat(T actual, Matcher<? super T> matcher)<T> void assertThat(String reason, T actual, Matcher<? super T> matcher)
assertThat为我们提供了一种全新的判断方式,能够判断给定的target(参数actual),是否满足我们自定义的规则组合(Matcher)。
Matcher是什么呢?Mather是一套匹配符,由JUnit4.4中引入的Hamcrest框架提供。这些匹配符更接近自然语言,可读性高,更加灵活;通过对Matcher的灵活组合,可以实现我们想要的任何测试。
import org.junit.Test;import static org.hamcrest.core.AnyOf.*;import static org.hamcrest.core.Is.*;import static org.hamcrest.core.IsEqual.*;import static org.hamcrest.core.StringContains.*;import static org.hamcrest.core.StringEndsWith.*;import static org.hamcrest.core.StringStartsWith.*;import static org.hamcrest.core.AllOf.*;import static org.junit.Assert.*;public class AssertThatTest { @Test public void test() throws Exception{ assertThat(evaluate(), allOf(startsWith("H"), endsWith("d"), containsString("World"))); assertThat(evaluate(), anyOf(isA(String.class), equalTo(100))); } //simulation biz method private String evaluate() { return "Hello World"; }}
上述程序中,我们用一个evaluate方法来模拟需要测试的业务方法,这个方法返回固定的字符串。在Test方法中,我们使用hamcrest框架提供的Matcher制定了两个规则,一个规则是返回值必须以字母H开头,以字母d结尾并且包含字符串World;另一个规则是返回值要么是一个字符串(String),要么等于数字100。我们使用assertThat来进行判断。很显然,我们的返回值是同时满足这两条规则的,因此该测试方法能够通过
结尾
Junit中还有一些东西没有介绍到,例如Theories、Categories,这里简要的提一下:Theories是一种更具灵活和表现力的断言,能够在特定的场景下捕获预期的行为,例如一个Test方法需要两个int参数进行多组测试,我们可以这样做
@Testpublic void multiplyTest( @TestedOn(ints ={0,5,10}) int first, @TestedOn(ints = {0, 1, 2}) int second ){ //....}
Categories用来给Test方法进行分类,这样我们可以通过指定需要运行的Categories,来部分运行单元测试。例如,我们通过@IncludeCategory(SuperClass.class)指明需要运行的分类为SuperClass.class,那么任何标记有@Category({SubClass.class}) 的测试方法都将运行。@Category注解可以标在任何类和接口上面。
- JUnit单元测试
- JUnit 单元测试
- 单元测试JUnit
- Junit单元测试
- Junit单元测试
- junit单元测试
- JUnit单元测试
- Junit单元测试
- JUnit单元测试
- junit单元测试
- Junit单元测试
- Junit 单元测试
- Junit单元测试
- JUnit单元测试
- JUnit单元测试
- jUnit 单元测试
- Junit单元测试
- junit 单元测试
- 数据库-MYSQL安装配置和删除
- 浅析ORM
- 03-树1 树的同构 (25分)
- Android使用RadioButton结合ListView显示对话框单选按钮列表
- poj 2239 Selecting Courses
- Junit单元测试
- 从这里开始吧
- (4)caffe总结之视觉层及参数
- 设定义一个类 整型数组的大小,表示可放元素的个数整型数组当前的元素个数,初始应为0,当elem等于size时,数组满
- 使用 Gradle 发布 AAR 到 Maven 仓库
- 自考那些事儿(九):再次学操作系统
- oracle sql复习
- 算法题:给出一组数字,拼接一个最大的值
- 解决ubuntu 14.04下,搜狗输入法无法输入中文的问题