引言

JUnit作为Java语言的测试框架,在测试驱动开发(TDD)下扮演重要的角色。众所周知,无论开发大型项目还是一般的小型项目,

单元测试都至关重要。单元测试为软件可发测试维护提供了很大的便利。JUnit 4 作为最新版本,增添了许多新的特性,

结合Hamcrest,可以写出很多灵活的测试。从JUnit 4 开始 jar包放在org.junit包下。代码已经托管在GitHub上。

为了以后测试方便,自定义了一个JUnit的类库,把几个重要的jar包导在一个类库,

这样,以后的项目工程需要写单元测试,直接导入这个类库,不需要重新导入各种jar包。

自定义类库结构如下:

关于如何自定义类库,Build Path -> Configure Build Path... -> Add Library... -> User Library... -> Next ......,

常规的单元测试,是在工程下新建一个Source Folder和src目录下相同的包名结构,测试A类,就在相对应的包下

创建一个测试类ATest,测试fun方法,就相应的创建测试方法testFun()。本文单纯学习JUnit特性,没有采用这种方式。

ok,废话到此为止,开始测试。文章略长,按需阅读。

断言 - Assertions

在JUnit 之前版本,经常会使用assertTrue,assertEquals,assertNull等断言,根据字面意思就很容易知道函数的用途。

JUnit 4 结合Hamcrest推出更强大的断言AssertThat(),相比于之前的assertXXX,assertThat代码风格将变得统一,更容易维护;

配合Hamcrest的Matchers, 可以写出很多灵活的测试,下面会提到;另一个优点更倾向于英语语法,不像"谓宾主"

(如:assertEquals(9, x)) 语法模式拗口,assertThat使用类型"主谓宾"的语法模式(如:assertThat(x, is(9)),可能是受母语的影响,

这条优点感触不是很深刻; 下面要说的这个优点比较实用,AssertThat测试失败会提供一些可读的描述性错误信息,

而assertXXX不会(当然可以手动去写),assertThat会举例说明; Last but not the least(v~v高考残留的英语记忆),

可发人员可以实现Matcher接口,自定义匹配符,这个功能很强大。

注意:测试的时候需要引入一些类,有些类(Assert, Matchers)比较特殊,采用静态引入的方式,

好处在于可以直接使用这些类的静态方法(assertTure, assertThat),而不用使用类名.方法名的方式。

assertXXX

  1. import static org.junit.Assert.*;
  2.  
  3. import org.junit.Test;
  4.  
  5. /**
  6. * AssertXXX测试
  7. * @author michael
  8. */
  9. public class AssertTests {
  10.  
  11. @Test
  12. public void testAssertArrayEquals() {
  13. byte[] expected = "trial".getBytes();
  14. byte[] actual = "trial".getBytes();
  15. assertArrayEquals("failure - byte arrays not same", expected, actual);
  16. }
  17.  
  18. @Test
  19. public void testEquals() {
  20. assertEquals("failure - strings are not equal", "text", "text");
  21. }
  22.  
  23. @Test
  24. public void testAssertTrue() {
  25. assertTrue("failure - should be true", true);
  26. }
  27.  
  28. @Test
  29. public void testFalse() {
  30. assertFalse("failure - should be false", false);
  31. }
  32.  
  33. @Test
  34. public void testAssertNotNull() {
  35. assertNotNull("should not be null", new Object());
  36. }
  37.  
  38. @Test
  39. public void testAssetNull() {
  40. assertNull("should be null", null);
  41. }
  42.  
  43. @Test
  44. public void testAssertNotSame() {
  45. assertNotSame("should not be same Object", new Object(), new Object());
  46. }
  47.  
  48. @Test
  49. public void testAssertSame() {
  50. Integer aNumber = Integer.valueOf(985);
  51. assertSame("should be same", aNumber, aNumber);
  52. }
  53.  
  54. }

assertThat

  1. import static org.junit.Assert.*;
  2. import static org.hamcrest.Matchers.*;
  3.  
  4. import java.util.Arrays;
  5.  
  6. import org.junit.Test;
  7.  
  8. public class AssertThatTests {
  9.  
  10. //JUnit Matchers assertThat
  11. @Test
  12. public void testAssertThatBothContainsString() {
  13. assertThat("albumen", both(containsString("a")).and(containsString("b")));
  14. }
  15.  
  16. @Test
  17. public void testAssertThathasItemsContainsString() {
  18. assertThat(Arrays.asList("one", "two", "three"), hasItems("one", "three"));
  19. }
  20.  
  21. @Test
  22. public void testAssertThatEveryItemContainsString() {
  23. assertThat(Arrays.asList(new String[] {"fun", "ban", "net"}), everyItem(containsString("n")));
  24. }
  25.  
  26. /**
  27. * Core Hamcrest Matchers with assertThat
  28. * 组合使用多种匹配符
  29. */
  30. @Test
  31. public void testAssertThatHamcrestCoreMatchers() {
  32. assertThat("good", allOf(equalTo("good"), startsWith("good")));
  33. assertThat("good", not(allOf(equalTo("good"), equalTo("bad"))));
  34. assertThat("good", anyOf(equalTo("good"), equalTo("bad")));
  35. assertThat(3, not(either(equalTo(6)).or(equalTo(9))));
  36. assertThat(new Object(), not(sameInstance(new Object())));
  37. }
  38.  
  39. /**
  40. * Readable failure message
  41. * assertThat会提供可读性的错误信息,assertTrue不会
  42. */
  43. @Test
  44. public void testFailureMessage() {
  45. String s = "coour";
  46. //assertTrue(s.contains("color") || s.contains("colour"));
  47. assertThat(s, anyOf(containsString("color"), containsString("colour")));
  48. }
  49.  
  50. }

套件测试 - Aggregating tests in Suites

如果你写了一系列的测试类(十几个甚至几十个),你不可能一个一个的去测试,此时,套件测试就会派上用场。

创建一个空类,不需要为这个类定义任何东西,只需要在这个类的头部添加注解@RunWith(Suite.class)。

@Suite.SuiteClasses。在SuiteClasses里添加需要测试的类。创建的这个类只是作为一个载体,承载上面的注解。

  1. import org.junit.runner.RunWith;
  2. import org.junit.runners.Suite;
  3.  
  4. import assertions.AssertTests;
  5. import assertions.AssertThatTests;
  6.  
  7. @RunWith(Suite.class)
  8. @Suite.SuiteClasses({
  9. //添加需要测试的类
  10. AssertTests.class,
  11. AssertThatTests.class
  12.  
  13. // more test classes
  14. })
  15.  
  16. public class FutureTestSuite {
  17.  
  18. /**
  19. * the class remains empty,
  20. * used only a holder for the above annotations.
  21. */
  22. }

What's the difference between failure and error in JUnit

顺序测试 - Test Execution Order

顺序测试的应用场景不是很多,当你想让你写的一系列测试方法按照一定的顺序执行才会用到。

添加@FixMethodOrder注解,有三种常用的运行顺序定义在枚举类MethodSorters内。

DEFAULT、JVM、NAME_ASCENDING。

DEFAULT:默认,比较方法名的hashCode值。

JVM:依赖JVM的实现,不同机器上可能有所不同。

NAME_ASCENDING:按照测试函数方法名升序。

  1. import org.junit.FixMethodOrder;
  2. import org.junit.Test;
  3. import org.junit.runners.MethodSorters;
  4.  
  5. @FixMethodOrder(MethodSorters.NAME_ASCENDING)
  6. //@FixMethodOrder(MethodSorters.DEFAULT)
  7. //@FixMethodOrder(MethodSorters.JVM)
  8. public class TestMethodOrder {
  9.  
  10. @Test
  11. public void testA() {
  12. System.out.println("first");
  13. }
  14.  
  15. @Test
  16. public void testB() {
  17. System.out.println("second");
  18. }
  19.  
  20. @Test
  21. public void testC() {
  22. System.out.println("third");
  23. }
  24.  
  25. }

异常测试 - Exception Testing

开发者在编写程序时,有些代码可能会抛出异常。

如何测试这些代码是否按照我们预想的抛出异常,此时就需要用到异常测试。

@Test注解有一个可选择的参数expected,用于指定可能抛出的异常。例如:

Expected Exceptions

  1. import java.util.ArrayList;
  2. import org.junit.Test;
  3.  
  4. public class TestException {
  5.  
  6. @SuppressWarnings("unused")
  7. private double result;
  8.  
  9. @Test(expected = ArithmeticException.class)
  10. public void divide() {
  11. result = 1/0;
  12. }
  13.  
  14. /**
  15. * The expected parameter should be used with care.
  16. * The test will pass if any code in the method throws IndexOutOfBoundsException
  17. */
  18. @Test(expected = IndexOutOfBoundsException.class)
  19. public void empty() {
  20. new ArrayList<Object>().get(0);
  21. }
  22.  
  23. }

不过,expected参数应当谨慎使用,因为,如果测试方法任意一处代码抛出expected指定的异常,

测试都会通过,无法准确定位哪处代码抛出的异常。长远考虑,推荐使用ExpectedException rule。

关于Rule会在另一篇文章中介绍。

ExceptedException rule

上面的测试方法对于测试简单的例子比较适用,但它存在一定的限制,比如开发者无法测试异常信息。

当然JUnit3.x 提供了Try/Catch Idiom可以预测异常和异常信息。JUnit4则提供了ExpectedException rule。

不同的是,JUnit4 expectedMessage除了可以预测可能抛出的异常信息,还可以与Hamcrest的Matchars配合 使用,

编写出更加灵活的测试。例如:

  1. import static org.hamcrest.Matchers.containsString;
  2.  
  3. import java.util.ArrayList;
  4. import java.util.List;
  5.  
  6. import org.junit.Rule;
  7. import org.junit.Test;
  8. import org.junit.rules.ExpectedException;
  9.  
  10. public class TestExceptionRule {
  11.  
  12. //Expected Exception Rule
  13. @Rule
  14. public ExpectedException thrown =ExpectedException.none();
  15.  
  16. /**
  17. * This rule lets you indicate not only what exception you are expecting,
  18. * but also the exception message you are expecting:
  19. */
  20. @Test
  21. public void shouldTestExceptionMessage() {
  22. List<Object> list = new ArrayList<Object>();
  23.  
  24. thrown.expect(IndexOutOfBoundsException.class);
  25. thrown.expectMessage("Index: 0, Size: 0");
  26. thrown.expectMessage(containsString("Size: 0"));
  27.  
  28. list.get(0);
  29. }
  30.  
  31. }

测试忽略 - Ignore tests

如果在某次测试中想要跳过某个测试方法,就使用@Ignore注解。

当然直接注释掉@Test注解同样可以跳过该测试方法,不同的是,

注释掉在测试结果中不会显示,而使用@Ignore会显示该方法被忽略。

  1. import org.junit.Ignore;
  2. import org.junit.Test;
  3.  
  4. public class TestIgnore {
  5.  
  6. @Test
  7. public void testA() {
  8. System.out.println("executed");
  9. }
  10.  
  11. @Ignore
  12. @Test
  13. public void testB() {
  14. System.out.println("ignored");
  15. }
  16.  
  17. }

超时测试 - Timeout for tests

超时测试,顾名思义,测试方法超过指定的时间就会报Errors,注意不是Failures。

Why is JUnit timeout an Error Not Failure

开发人员可以为单独某个方法设置超时时间,也可以为整个测试类设置统一超时时间。

单独为一个方法设置超时时间,单位毫秒

  1. import org.junit.Test;
  2.  
  3. public class TestTimeout {
  4.  
  5. @Test(timeout=100)
  6. public void testWithTimeOut() {
  7.  
  8. for( ; ;) {
  9.  
  10. }
  11.  
  12. }
  13.  
  14. }

为整个类设置超时时间,单位毫秒

  1. import org.junit.Rule;
  2. import org.junit.Test;
  3. import org.junit.rules.Timeout;
  4.  
  5. public class TestTimeoutRule {
  6.  
  7. public static int n;
  8.  
  9. @Rule
  10. public Timeout globalTimeout = new Timeout(100);
  11.  
  12. @Test
  13. public void testInfiniteLoop1() {
  14. n++;
  15. for( ; ;) {
  16.  
  17. }
  18. }
  19.  
  20. @Test
  21. public void testInfiniteLoop2() {
  22. n++;
  23. for( ; ;) {
  24.  
  25. }
  26. }
  27.  
  28. }

结束语

Keeps the bar green to keep the code clean.

JUnit4单元测试基础篇的更多相关文章

  1. junit4单元测试基础

    导入方法看如下截图就明白了:   新建测试用例 右击包名,点击新建,或者新建里的others,选择JUnit test case,如下图所示:   接下来,给测试类起名字和选择要测试的类,如下图所示: ...

  2. DDD分层架构之领域实体(基础篇)

    DDD分层架构之领域实体(基础篇) 上一篇,我介绍了自己在DDD分层架构方面的一些感想,本文开始介绍领域层的实体,代码主要参考自<领域驱动设计C#2008实现>,另外参考了网上找到的一些示 ...

  3. python面试题库——1Python基础篇

    第一部分 Python基础篇(80题) 为什么学习Python? 语言本身简洁,优美,功能超级强大,跨平台,从桌面应用,web开发,自动化测试运维,爬虫,人工智能,大数据处理都能做 Python和Ja ...

  4. SpringBoot使用Junit4单元测试

    SpringBoot2.0笔记 本篇介绍Springboot单元测试的一些基本操作,有人说一个合格的程序员必须熟练使用单元测试,接下来我们一起在Springboot项目中整合Junit4单元测试. 本 ...

  5. 你所不知道的库存超限做法 服务器一般达到多少qps比较好[转] JAVA格物致知基础篇:你所不知道的返回码 深入了解EntityFramework Core 2.1延迟加载(Lazy Loading) EntityFramework 6.x和EntityFramework Core关系映射中导航属性必须是public? 藏在正则表达式里的陷阱 两道面试题,带你解析Java类加载机制

    你所不知道的库存超限做法 在互联网企业中,限购的做法,多种多样,有的别出心裁,有的因循守旧,但是种种做法皆想达到的目的,无外乎几种,商品卖的完,系统抗的住,库存不超限.虽然短短数语,却有着说不完,道不 ...

  6. SpringBoot图文教程「概念+案例 思维导图」「基础篇上」

    有天上飞的概念,就要有落地的实现 概念+代码实现是本文的特点,教程将涵盖完整的图文教程,代码案例 每个知识点配套自测面试题,学完技术自我测试 本文初学向,所以希望文中所有的代码案例都能敲一遍 大哥大姐 ...

  7. 面试题之第一部分(Python基础篇) 80题

    第一部分(python基础篇)80题 为什么学习Python?==*== # 1. python应用于很多领域,比如后端,前端,爬虫,机器学习(人工智能)等方面,几乎能涵盖各个开发语言的领域,同时它相 ...

  8. Python自动化测试面试题-Python基础篇

    目录 Python自动化测试面试题-经验篇 Python自动化测试面试题-用例设计篇 Python自动化测试面试题-Linux篇 Python自动化测试面试题-MySQL篇 Python自动化测试面试 ...

  9. 十一、Abp vNext 基础篇丨测试

    前言 祝大家国庆快乐,本来想国庆之前更新完的,结果没写完,今天把剩下的代码补了一下总算ok了. 本章节也是我们后端日常开发中最重要的一步就是测试,我们经常听到的单元测试.集成测试.UI测试.系统测试, ...

随机推荐

  1. Jqury笔记

    1.  --------------- -var aa = new Array(); aa.push(1); alert(aa[0]); var aa=[];也表示一个数组: ------------ ...

  2. OpenGL Development Cookbook chapter7部分翻译

    让我们通过以下简单步骤开始我们的配方: 1.通过读取外部的体数据文件,并通过该加载数据集数据转换成一个OpenGL纹理.也使硬件的mipmap生成.通常情况下,从使用一个横截面中获得的体积数据文件存储 ...

  3. 如何在ubuntu 12.04下搭建Python Django环境

    1. 检查python是否安装:直接在shell里输入python,如果已经安装了python,即可进入python bash,并看到版本号(如Python 2.7.3) ——在ubuntu中pyth ...

  4. MVC Filter 实现方式和作用范围控制

    Asp.Net MVC Filter 实现方式和作用范围控制 MVC中的Filte 简单又优雅的实现了AOP ,在日志,权限,缓存和异常处理等方面用的比较多.但本文不是讨论Filter这些功能点,而是 ...

  5. Euclid Problem - PC110703

    欢迎访问我的新博客:http://www.milkcu.com/blog/ 原文地址:http://www.milkcu.com/blog/archives/uva10104.html 原创:Eucl ...

  6. ASP.Net页面传值比较

    ASP.Net页面传值比较   作为一个ASP.Net程序员,尤其是搞B/S开发的,对于不同页面之间变量值的传递用的非常广泛,而掌握不同方式之间的区别和特点也就很有必要.本文将针对这一知识点做一个简单 ...

  7. NUnit详细使用方法

    http://www.ltesting.net/ceshi/open/kydycsgj/nunit/ http://nunit.org/index.php?p=download NUnit详细使用方法 ...

  8. ASP.NET WEB API构建基于REST风格

    使用ASP.NET WEB API构建基于REST风格的服务实战系列教程[开篇] 最近发现web api很火,园内也有各种大神已经在研究,本人在asp.net官网上看到一个系列教程,原文地址:http ...

  9. twitter 授权过程

    转自:http://blog.csdn.net/yangjian8915/article/details/11816669 官方的流程图如下: 下面开始一步步讲解,如何获取最终的access_toke ...

  10. C# 利用反射动态创建对象——带参数的构造函数和String类型

    C# 利用反射动态创建对象——带参数的构造函数和String类型 最近笔者有一个想法需要利用反射动态创建对象(如string,int,float,bool,以及自定义类等)来实现,一直感觉反射用不好, ...