首页 > 代码库 > 单元测试——junit

单元测试——junit

一、前言

    JUnit4对于JUnit3来说是一个历史性的改革。因为JUnit4引入了注解(annotation),通过解析注解就可以为测试提供相应的信息,抛弃JUnit3使用命名约束以及反射机制的方法。但是JUnit4.x与JUnit3.8向后是兼容的。

官网:http://junit.org/junit4/

预备知识点:

源文件夹:在eclipse中在项目中,右键新建选项中有一个源文件夹(Source folder)。其与普通文件夹的不同点表现在,他的图标是十字型的包裹图案。

源文件夹的特点:在源文件夹中的文件会自动转换为class文件存在于当前项目的bin目录中。

在eclipse中可以在需要测试的类文件上右击->新建->JUnit测试用例->创建测试用例(可以选择版本,文件夹路径,以及生成的其它细节)

计算机并不能准确地表示所有的浮点数,通常都会有一些偏差。因此如果用断言来比较浮点数,则需要制定一个额外的误差参数(tolerance)。

建议:

  1. 不建议使用eclipse集成的JUnit,因为其版本是固定的,相对而言可能一些新特性是不支持的。
  2. 测试代码放在与src同级的另外一个源文件夹中
  3. 测试文件的路径结构应与被测试的源文件相同
  4. 测试文件的命名:Test+源文件名称

junit3和junit4的使用区别:

  1. 在JUnit3中需要继承TestCase类,但在JUnit4中已经不需要继承TestCase
  2. 在JUnit3中需要覆盖TestCase中的setUp和tearDown方法,其中setUp方法会在测试执行前被调用以完成初始化工作,而tearDown方法则在结束测试结果时被调用,用于释放测试使用中的资源,而在JUnit4中,只需要在方法前加上@Before,@After
  3. 在JUnit3中对某个方法进行测试时,测试方法的命令是固定的,例如对addBook这个方法进行测试,需要编写名字为testAddBook的测试方法,而在JUnit4中没有方法命令的约束,在方法的前面加上@Test,这就代表这个方法是测试用例中的测试方法
  4. 新的断言assertThat
  5.  @BeforeClass 和 @AfterClass 。在JUnit3,如果所有的test case仅调用一次setUp()和tearDown()需要使用TestSetup类
  6. 测试异常处理@Test(expected = DataFormatException.class)
  7. 设置超时@Test(timeout = 1000)
  8. 忽略测试@Ignore
  9. 集成测试

如何尽量提高测试代码的兼容性:

  • JUnit4中初始化函数命名为setUp(),释放资源的函数命名为tearDown()
  • JUnit4中测试函数一test
  • 为了在JUnit4中像JUnit3中直接使用assert(断言),我们要静态导入Assert类(import static org.junit.Assert.*;)


测试代码需要做的事情:

  • 准备测试所需要的各种条件(创建所有必须的对象,分配必要的资源等等)
  • 调用要测试的方法
  • 验证被测试方法的行为和期望是否一致
  • 完成后清理各种资源


二、JUnit3使用

推荐图书:《单元测试之道Java版:使用JUnit》

随书代码:http://download.csdn.net/detail/fanxiaobin577328725/9632002

JUnit3采用的是反射机制,所以对名字的命名非常重要。

1.测试方法必须在原方法的方法首部添加Test,测试类没有这样强制要求。

2.测试类必须继承TestCase。

3.测试方法覆盖setUp和tearDown方法来初始化和释放资源。(测试每一个方法都会执行一次setUp和tearDown方法

技术分享

OneTimeSetUp是什么?没关系向下看就可以了,下面将给出详细解释。

断言方法:(因为继承了TestCase,而TestCase继承了Assert类,所以直接使用断言)

基本上:下列函数的第一个参数可以是可选的信息参数。

常用断言方法
常用方法解释
assertArrayEquals(expecteds, actuals)查看两个数组是否相等
assertEquals(expected, actual)查看两个对象是否相等,类似于字符串比较使用的equals()方法
assertEquals(String message,expected, actual)与上面一样,就是在出错时显示message信息。
assertNotEquals(first, second)查看两个对象是否不相等。
assertNull(object)查看对象是否为空
assertNotNull(object)查看对象是否不为空
assertSame(expected, actual)查看两个对象的引用是否相等。类似于使用“==”比较两个对象
assertNotSame(unexpected, actual)查看两个对象的引用是否不相等。类似于使用“!=”比较两个对象
assertTrue(condition)查看运行结果是否为true
assertFalse(condition)查看运行结果是否为false
assertThat(actual, matcher)查看实际值是否满足指定的条件
fail()让测试失败














默认情况下,JUnit会自动运行所有的test...方法。然而任何测试类都能包含一个名为suite的静态方法:

public static Test suite() {
    TestSuite suite = new TestSuite();
  //想要测试的代码  
<pre name="code" class="java">  //.......
  return suite;
}

suite方法不仅可以调用当前测试类中的指定测试函数,还可以调用其他测试类以及其他测试类中的suite方法。

示例:

/**
 * Excerpted from the book, "Pragmatic Unit Testing in Java with JUnit"
 * ISBN 0-9745140-1-2
 * Copyright 2003 The Pragmatic Programmers, LLC.  All Rights Reserved.
 * Visit www.PragmaticProgrammer.com
 */

/**
 * A brute-force solution to the Travelling Salesman Problem.
 *
 * It will run in exponential time.
 *
 * Better solutions use genetic algorithms and such.
 */

public class TSP {

  /**
   * For the top numCities in our territory, compute the shortest path
   * and return thr total number of miles in that path.
   */
  public int shortestPath(int numCities) {
    // Ha!  Mock object! 
    switch (numCities) {
    case 50: return 2300;
    case 5: return 140;
    case 10: return 586;
    }
    return 0;
  }

  /**
  * Load the cities for the given territory and lock them
  */
  public void loadCities(String name) {
  }

  /**
  * Relase the lock on cities in this territory
  */
  public void releaseCities() {
  }

}
测试代码:(TestClassTwo)

/**
 * Excerpted from the book, "Pragmatic Unit Testing in Java with JUnit"
 * ISBN 0-9745140-1-2
 * Copyright 2003 The Pragmatic Programmers, LLC.  All Rights Reserved.
 * Visit www.PragmaticProgrammer.com
 */

import junit.framework.*;

public class TestClassTwo extends TestCase {
  
  public TestClassTwo(String method) {
    super(method);
  }
  
  // This one takes a few hours...
  public void testLongRunner() {
    TSP tsp = new TSP(); // Load with default cities
    assertEquals(2300, tsp.shortestPath(50)); // top 50
  }
  
  public void testShortTest() {
    TSP tsp = new TSP(); // Load with default cities
    assertEquals(140, tsp.shortestPath(5)); // top 5
  }
  
  public void testAnotherShortTest() {
    TSP tsp = new TSP(); // Load with default cities
    assertEquals(586, tsp.shortestPath(10)); // top 10
  }
  
  public static Test suite() {
    TestSuite suite = new TestSuite();
    // Only include short tests
    suite.addTest(
          new TestClassTwo("testShortTest"));
    suite.addTest(
          new TestClassTwo("testAnotherShortTest"));
    return suite;
  }
  
}
TestClassTwo的运行结果:

技术分享

 测试结果显示:testLongRunner()没有运行。

 测试代码:(TestClassOne)

/**
 * Excerpted from the book, "Pragmatic Unit Testing in Java with JUnit"
 * ISBN 0-9745140-1-2
 * Copyright 2003 The Pragmatic Programmers, LLC.  All Rights Reserved.
 * Visit www.PragmaticProgrammer.com
 */

import junit.framework.*;

public class TestClassOne extends TestCase {

  public TestClassOne(String method) {
    super(method);
  }
  
  public void testAddition() {
    assertEquals(4, 2+2);
  }
  
  public void testSubtraction() {
    assertEquals(0, 2-2);
  }
  
}
测试代码:(TestClassComposite)
/**
 * Excerpted from the book, "Pragmatic Unit Testing in Java with JUnit"
 * ISBN 0-9745140-1-2
 * Copyright 2003 The Pragmatic Programmers, LLC.  All Rights Reserved.
 * Visit www.PragmaticProgrammer.com
 */

import junit.framework.*;

public class TestClassComposite extends TestCase {
  
  public TestClassComposite(String method) {
    super(method);
  }
  
  static public Test suite() {
    TestSuite suite = new TestSuite();
    // Grab everything:
    suite.addTestSuite(TestClassOne.class); 
    // Use the suite method:
    suite.addTest(TestClassTwo.suite()); 
    return suite;
  }
  
}
TestClassComposite的运行结果:
技术分享
 测试结果显示:其执行了TestClassOne中的所有测试方法,执行了TestClassTwo中的suite方法。

 上面我们了解到每一个方法执行前后都会有一个setUp和tearDown方法执行,然而suite的初始化以及清理工作与方法是分开的。过程相对复杂了点,需要提供所需测试的一个suite(无论通过什么样的方法)并且把它包装进一个TestSetup对象。

在上面代码的基础上进行的更改:

/**
 * Excerpted from the book, "Pragmatic Unit Testing in Java with JUnit"
 * ISBN 0-9745140-1-2
 * Copyright 2003 The Pragmatic Programmers, LLC.  All Rights Reserved.
 * Visit www.PragmaticProgrammer.com
 */

import junit.framework.*;
import junit.extensions.*;

public class TestClassTwo extends TestCase {

    private static TSP tsp;

    public TestClassTwo(String method) {
        super(method);
    }

    // This one takes a few hours...
    public void testLongRunner() {
        System.out.println("testLongRunner method running");
        assertEquals(2300, tsp.shortestPath(50));
    }

    public void testShortTest() {
        System.out.println("testShortTest method running");
        assertEquals(140, tsp.shortestPath(5));
    }

    public void testAnotherShortTest() {
        System.out.println("testAnotherShortTest method running");
        assertEquals(586, tsp.shortestPath(10));
    }

    public static Test suite() {
        TestSuite suite = new TestSuite();
        // Only include short tests
        suite.addTest(new TestClassTwo("testShortTest"));
        suite.addTest(new TestClassTwo("testAnotherShortTest"));

        TestSetup wrapper = new TestSetup(suite) {
            protected void setUp() {
                oneTimeSetUp();
            }

            protected void tearDown() {
                oneTimeTearDown();
            }
        };

        return wrapper;
    }

    public static void oneTimeSetUp() {
        // one-time initialization code goes here...
        System.out.println("suite_up......");
        tsp = new TSP();
        tsp.loadCities("EasternSeaboard");
    }

    public static void oneTimeTearDown() {
        // one-time cleanup code goes here...
        System.out.println("suite_down......");
        tsp.releaseCities();
    }

    @Override
    protected void setUp() throws Exception {
        // TODO 自动生成的方法存根
        System.out.println("setUp.....");
        super.setUp();
    }

    @Override
    protected void tearDown() throws Exception {
        // TODO 自动生成的方法存根
        System.out.println("tearDown.....");
        super.tearDown();
    }

}
运行结果:

suite_up......
setUp.....
testShortTest method running
tearDown.....
setUp.....
testAnotherShortTest method running
tearDown.....
suite_down......
注意:
  你可以在同一个类中同时使用方法和suite的setup()和teardown()。

三、JUnit4使用

Junit4采用注释的机制,所以名字的命名并不那么重要了,但是最好规范一下。

命名规范:测试方法以Test开头

在JUnit4中可以有setUp和tearDown方法,但是测试方法的时候不会被执行。

在JUnit4.x中想直接使用Assert方法,则需要静态导入Assert:import static org.junit.Assert.*;

在JUnit4.x中测试类可以是一个普通类,也可以去继承一个类或者实现一个接口,要实现测试,只需要在要测试的方法之前加上@Test注释。


常用注解:

  • @Before:在每个测试之前运行的代码
  • @After :在每个测试之后运行的代码
  • @Test:测试代码
  • @Ignore:忽略的测试方法
  • @BeforeClass:针对所有测试,只执行一次,且必须为static void
  • @AfterClass:针对所有测试,只执行一次,且必须为static void
  • @RunWith:指定测试类使用某个运行器
  • @Parameters:指定测试类的测试数据集合
  • @Rule:允许灵活添加或重新定义测试类中的每个测试方法的行为
  • @FixMethodOrder:指定测试方法的执行顺序

@Test可以有两个参数:

  1. expected:检测方法是否抛出指定的异常(未抛出报错误)
  2. timeout:方法执行操作所耗费的毫秒数(超出指定时间报错误)

一个测试类单元测试的执行顺序为:
    @BeforeClass –> @Before –> @Test –> @After –> @AfterClass

每一个测试方法的调用顺序为:
   
@Before –> @Test –> @After


通过测试组件测试多个类:

    指定测试组件使用@RunWith




JUnit4新增的两个断言:



四、Hamcrest框架

镜像网站:http://hamcrest.org/

目的:增强JUnit,用一些语义通俗的方法来进行测试。要使用JUnit中的assertThat来进行断言。

需要静态导入Matchers包(import static org.hamcrest.Matchers.*;),否则就需要通过函数名称名称来调用了。



五、其它细节

1.测试

测试的具体方面:Right-BICEP

  • Right——结果是否正确?
  • B——是否所有的边界条件都是正确的?
  • I——能查一下反向关联吗?
  • C——能用其他手段交叉检查一下结果吗?
  • E——你是否可以强制错误条件发生?
  • P——是否满足性能要求?
<1>结果的正确性

     无论如何,必须确定代码所做的和你所期望的一致。我们需要尽可能的从需求说明中获取信息,来全面设计测试方法,有时候文档是不明了的或者不完整的,至少我们可以自己发明一些需求来。从客户角度来看问题,而且要根据用户的反馈来调整自己的假设。在代码的整个生命周期中,“正确”的定义可能会不断在改变。

    针对有大量测试数据的测试,我们会考虑用一个独立的数据文件来存储这些测试数据,然后让单元测试读取该文件。这是一个很好地选择,但是测试数据很有可能是不正确的,实际上测试数据比代码更有可能出错。因此当测试数据显示错误发生的时候,我们应该在怀疑代码之前先对测试数据检查两三遍。

     另外,还有一些值得考虑的:代码本身是否并没有测试任何异常的情况。要实现这个功能,需要怎么来做呢?

     一个原则是:对于验证被测方法是正确的这件事情,如果某些做法能够使它更加容易,那么就采纳它吧。

<2>边界条件(CORRECT Boundary Conditions)

     找边界条件是做单元测试中最有价值的工作之一,因为bug一般就出现在边界上。

一些需要我们考虑的条件有:

  • 完全伪造或者不一致的输入数据,例如一个名为“!*W:X\&Gi/w->g/h#WQ@”的文件。
  • 格式错误的数据,例如没有顶级域名的电子邮件地址,就像fred@foobar这样的。
  • 空值或者不完整的值(如0.0.0. “”和null)
  • 一些与意料中的合理值想去甚远的数值。例如一个人的随书为10000岁
  • 如果需求的是一个不允许出现重复数值的list,但是传入的是一个存在重复数值的list。
  • 如果要求的是一个有序list,但是传入的是一个无序的list;或者反之。
  • 事情达到的次序是错误的,或者碰巧和期望的次序不一致。例如,在未登录系统之前,就尝试打印文档

下面是测试边界接条件所需要考虑的几大反面:

  • 一致性(Conformance)——值是否和预期的一致。
  • 顺序性(Ordering)——值是否如应该的那样,是有序或者无序的。
  • 区间性(Range)——值是否位于合理的最小值和最大值之内。
  • 依赖性(Reference)——代码是否引用了一些不在代码本身控制范围之内的外部资源。
  • 存在性(Existence)——值是否存在(例如,是否是非null,非0,在一个集合中等等)。
  • 基数性(Cardinatity)——是否恰好有足够的值?
  • 相对或者绝对的时间性(Time)——所有事情的发生是否是有序的?是否是在正确的时刻?是否恰好及时?


<3>检查反向关联

     对于一些方法,我们可以使用反向的逻辑关系来验证他们。

例如:我们可以用对结果进行平方的方式来检查一个计算平方根的函数,判断测试结果是否和元数据是否接近;为了检查某条记录是否成功地插入了数据库,你也可以通过查询这条记录来验证。等等

      需要注意的是:当你同时编写了原方法和反向测试时,一些bug可能会被在连个函数中都出现的错误所覆盖。在可能的情况下,应该使用不同的原理来编写反向测试。


<4>使用其他手段来实现交叉检查

      通常来说,计算一个量会有一种以上的算法。我们可能基于效率或者其他特性来选择算法。但是测试中我们没必要考虑这些,所以我们可以用剩下的算法中的一个来交叉检测结果。


<5>强制产生错误条件

      真实世界中,错误总是会发生:磁盘会满,网络连接会断开,电子邮件会多的像掉进了黑洞,而长须会崩溃。你应该通过强制引发错误,来测试你的代码是如何处理所有这些真实世界中的问题的。

我们存在的环境因素有:

  • 内存耗光
  • 磁盘用满
  • 时钟出问题
  • 网络不可用或者有问题
  • 系统过载
  • 调色板颜色数目有限
  • 显示分辨率过高或者过低


<6>性能特性

     一个检查起来会很有益处的部分是性能特性,而不是性能本身。

2.测试原则

  1. 建议创建一个站们的source folder-->test来编写测试类代码
  2. 测试类的包应该保持和需要测试的类的一致性
  3. 测试单元中的每一个测试方法都必须可以独立运行,没有顺序,而且测试方法之间不能有任何的依赖性



3.Mock测试

简介:mock测试就是在测试过程中,对于某些不容易构造或者不容易获取的对象,用一个虚拟的对象来创建以便测试的测试方法。

  • 真实对象具有不可确定的行为(产生不可预测的结果,如股票行情)
  • 真实对象很难被常见
  • 真实对象的某些行为很难出发(如网络错误)
  • 真实对象令程序的运行速度很慢
  • 真实对象有(或者是)用户界面
  • 测试需要询问真实对象他是如何被调用的(例如,测试可能需要验证某个回调函数是否被调用了)
  • 真实对象实际上并不存在(当需要和其他开发小组,或者新的硬件系统打交道的时候,这是一个普遍问题)

     借助于mock对象,我们就可以解决上面的所有问题。在使用mock对象进行测试的时候,总共有3个关键步骤,分别是:

  1. 使用一个借口来描述这个对象
  2. 为产品代码实现这个接口
  3. 以测试为目的,在mock对象中实现这个接口

     因为被测试代码只会通过接口来引用对象,所以它完全可以不知道它引用的究竟是真实对象还是mock对象。

下面是一个详细的示例:(具体讲解在《单元测试之道Java版:使用JUnit》这里不再详细叙述)

Environmental.java

/**
 * Excerpted from the book, "Pragmatic Unit Testing in Java with JUnit"
 * ISBN 0-9745140-1-2
 * Copyright 2003 The Pragmatic Programmers, LLC.  All Rights Reserved.
 * Visit www.PragmaticProgrammer.com
 */

/**
 * Excerpted from the book, "Pragmatic Unit Testing in Java with JUnit"
 * ISBN 0-9745140-1-2
 * Copyright 2003 The Pragmatic Programmers, LLC.  All Rights Reserved.
 * Visit www.PragmaticProgrammer.com
 */

/**
 * Excerpted from the book, "Pragmatic Unit Testing in Java with JUnit"
 * ISBN 0-9745140-1-2
 * Copyright 2003 The Pragmatic Programmers, LLC.  All Rights Reserved.
 * Visit www.PragmaticProgrammer.com
 */

/**
 * Excerpted from the book, "Pragmatic Unit Testing in Java with JUnit"
 * ISBN 0-9745140-1-2
 * Copyright 2003 The Pragmatic Programmers, LLC.  All Rights Reserved.
 * Visit www.PragmaticProgrammer.com
 */

/**
 * Excerpted from the book, "Pragmatic Unit Testing in Java with JUnit"
 * ISBN 0-9745140-1-2
 * Copyright 2003 The Pragmatic Programmers, LLC.  All Rights Reserved.
 * Visit www.PragmaticProgrammer.com
 */

/**
 * Excerpted from the book, "Pragmatic Unit Testing"
 * ISBN 0-9745140-1-2
 * Copyright 2003 The Pragmatic Programmers, LLC.  All Rights Reserved.
 * Visit www.PragmaticProgrammer.com
 */

/**
 * Various methods particular to our runtime environment
 */

public interface Environmental {
	public long getTime();
	// Other methods omitted...

	public void playWavFile(String name);

}

SystemEnvironment.java

/**
 * Excerpted from the book, "Pragmatic Unit Testing in Java with JUnit"
 * ISBN 0-9745140-1-2
 * Copyright 2003 The Pragmatic Programmers, LLC.  All Rights Reserved.
 * Visit www.PragmaticProgrammer.com
 */

/**
 * Excerpted from the book, "Pragmatic Unit Testing in Java with JUnit"
 * ISBN 0-9745140-1-2
 * Copyright 2003 The Pragmatic Programmers, LLC.  All Rights Reserved.
 * Visit www.PragmaticProgrammer.com
 */

/**
 * Excerpted from the book, "Pragmatic Unit Testing in Java with JUnit"
 * ISBN 0-9745140-1-2
 * Copyright 2003 The Pragmatic Programmers, LLC.  All Rights Reserved.
 * Visit www.PragmaticProgrammer.com
 */

/**
 * Excerpted from the book, "Pragmatic Unit Testing in Java with JUnit"
 * ISBN 0-9745140-1-2
 * Copyright 2003 The Pragmatic Programmers, LLC.  All Rights Reserved.
 * Visit www.PragmaticProgrammer.com
 */

/**
 * Excerpted from the book, "Pragmatic Unit Testing in Java with JUnit"
 * ISBN 0-9745140-1-2
 * Copyright 2003 The Pragmatic Programmers, LLC.  All Rights Reserved.
 * Visit www.PragmaticProgrammer.com
 */

/**
 * Excerpted from the book, "Pragmatic Unit Testing" ISBN 0-9745140-1-2
 * Copyright 2003 The Pragmatic Programmers, LLC. All Rights Reserved. Visit
 * www.PragmaticProgrammer.com
 */

public class SystemEnvironment implements Environmental {
	public long getTime() {
		return System.currentTimeMillis();
	}
	// other methods ...

	public void playWavFile(String name) {
		// Left as an exercise to the reader...
	}

}
MockSystemEnvironment.java

/**
 * Excerpted from the book, "Pragmatic Unit Testing in Java with JUnit"
 * ISBN 0-9745140-1-2
 * Copyright 2003 The Pragmatic Programmers, LLC.  All Rights Reserved.
 * Visit www.PragmaticProgrammer.com
 */

/**
 * Excerpted from the book, "Pragmatic Unit Testing in Java with JUnit"
 * ISBN 0-9745140-1-2
 * Copyright 2003 The Pragmatic Programmers, LLC.  All Rights Reserved.
 * Visit www.PragmaticProgrammer.com
 */

/**
 * Excerpted from the book, "Pragmatic Unit Testing in Java with JUnit"
 * ISBN 0-9745140-1-2
 * Copyright 2003 The Pragmatic Programmers, LLC.  All Rights Reserved.
 * Visit www.PragmaticProgrammer.com
 */

/**
 * Excerpted from the book, "Pragmatic Unit Testing in Java with JUnit"
 * ISBN 0-9745140-1-2
 * Copyright 2003 The Pragmatic Programmers, LLC.  All Rights Reserved.
 * Visit www.PragmaticProgrammer.com
 */

/**
 * Excerpted from the book, "Pragmatic Unit Testing in Java with JUnit"
 * ISBN 0-9745140-1-2
 * Copyright 2003 The Pragmatic Programmers, LLC.  All Rights Reserved.
 * Visit www.PragmaticProgrammer.com
 */

/**
 * Excerpted from the book, "Pragmatic Unit Testing" ISBN 0-9745140-1-2
 * Copyright 2003 The Pragmatic Programmers, LLC. All Rights Reserved. Visit
 * www.PragmaticProgrammer.com
 */

public class MockSystemEnvironment implements Environmental {
	private long current_time;
	private boolean playedWav = false;
	public long getTime() {
		return current_time;
	}

	public void setTime(long aTime) {
		current_time = aTime;
	}

	public void playWavFile(String filename) {
		playedWav = true;
	}

	public boolean wavWasPlayed() {
		return playedWav;
	}

	public void resetWav() {
		playedWav = false;
	}

	// ...
}
Checker.java

/**
 * Excerpted from the book, "Pragmatic Unit Testing in Java with JUnit"
 * ISBN 0-9745140-1-2
 * Copyright 2003 The Pragmatic Programmers, LLC.  All Rights Reserved.
 * Visit www.PragmaticProgrammer.com
 */

/**
 * Excerpted from the book, "Pragmatic Unit Testing in Java with JUnit"
 * ISBN 0-9745140-1-2
 * Copyright 2003 The Pragmatic Programmers, LLC.  All Rights Reserved.
 * Visit www.PragmaticProgrammer.com
 */

/**
 * Excerpted from the book, "Pragmatic Unit Testing in Java with JUnit"
 * ISBN 0-9745140-1-2
 * Copyright 2003 The Pragmatic Programmers, LLC.  All Rights Reserved.
 * Visit www.PragmaticProgrammer.com
 */

/**
 * Excerpted from the book, "Pragmatic Unit Testing in Java with JUnit"
 * ISBN 0-9745140-1-2
 * Copyright 2003 The Pragmatic Programmers, LLC.  All Rights Reserved.
 * Visit www.PragmaticProgrammer.com
 */

/**
 * Excerpted from the book, "Pragmatic Unit Testing in Java with JUnit"
 * ISBN 0-9745140-1-2
 * Copyright 2003 The Pragmatic Programmers, LLC.  All Rights Reserved.
 * Visit www.PragmaticProgrammer.com
 */

/**
 * Excerpted from the book, "Pragmatic Unit Testing"
 * ISBN 0-9745140-1-2
 * Copyright 2003 The Pragmatic Programmers, LLC.  All Rights Reserved.
 * Visit www.PragmaticProgrammer.com
 */

import java.util.Calendar;

public class Checker {

	public Checker(Environmental anEnv) {
		env = anEnv;
	}

	/**
	 * After 5 o'clock, remind people to go home by playing a whistle
	 */
	public void reminder() {
		Calendar cal = Calendar.getInstance();
		cal.setTimeInMillis(env.getTime());
		int hour = cal.get(Calendar.HOUR_OF_DAY);

		if (hour >= 17) { // 5:00PM
			env.playWavFile("quit_whistle.wav");
		}

	}

	// ...

	private Environmental env;
}
TestChecker.java
/**
 * Excerpted from the book, "Pragmatic Unit Testing in Java with JUnit"
 * ISBN 0-9745140-1-2
 * Copyright 2003 The Pragmatic Programmers, LLC.  All Rights Reserved.
 * Visit www.PragmaticProgrammer.com
 */

/**
 * Excerpted from the book, "Pragmatic Unit Testing in Java with JUnit"
 * ISBN 0-9745140-1-2
 * Copyright 2003 The Pragmatic Programmers, LLC.  All Rights Reserved.
 * Visit www.PragmaticProgrammer.com
 */

/**
 * Excerpted from the book, "Pragmatic Unit Testing in Java with JUnit"
 * ISBN 0-9745140-1-2
 * Copyright 2003 The Pragmatic Programmers, LLC.  All Rights Reserved.
 * Visit www.PragmaticProgrammer.com
 */

/**
 * Excerpted from the book, "Pragmatic Unit Testing in Java with JUnit"
 * ISBN 0-9745140-1-2
 * Copyright 2003 The Pragmatic Programmers, LLC.  All Rights Reserved.
 * Visit www.PragmaticProgrammer.com
 */

/**
 * Excerpted from the book, "Pragmatic Unit Testing in Java with JUnit"
 * ISBN 0-9745140-1-2
 * Copyright 2003 The Pragmatic Programmers, LLC.  All Rights Reserved.
 * Visit www.PragmaticProgrammer.com
 */

/**
 * Excerpted from the book, "Pragmatic Unit Testing"
 * ISBN 0-9745140-1-2
 * Copyright 2003 The Pragmatic Programmers, LLC.  All Rights Reserved.
 * Visit www.PragmaticProgrammer.com
 */

import junit.framework.*;
import java.util.Calendar;

public class TestChecker extends TestCase {

	public void testQuittingTime() {

		MockSystemEnvironment env = new MockSystemEnvironment();

		// Set up a target test time
		Calendar cal = Calendar.getInstance();
		cal.set(Calendar.YEAR, 2004);
		cal.set(Calendar.MONTH, 10);
		cal.set(Calendar.DAY_OF_MONTH, 1);
		cal.set(Calendar.HOUR_OF_DAY, 16);
		cal.set(Calendar.MINUTE, 55);
		long t1 = cal.getTimeInMillis();

		env.setTime(t1);

		Checker checker = new Checker(env);

		// Run the checker
		checker.reminder();

		// Nothing should have been played yet
		assertFalse(env.wavWasPlayed());

		// Advance the time by 5 minutes
		t1 += (5 * 60 * 1000);
		env.setTime(t1);

		// Now run the checker
		checker.reminder();

		// Should have played now
		assertTrue(env.wavWasPlayed());

		// Reset the flag so we can try again
		env.resetWav();

		// Advance the time by 2 hours and check
		t1 += 2 * 60 * 60 * 1000;
		env.setTime(t1);

		checker.reminder();
		assertTrue(env.wavWasPlayed());
	}
}


测试Servlet
    基于Web的Servlet需要容器(常用的是Tomcat)和浏览器,运行过程和环境相对复杂,这是一个交互的过程,看起来无法完成自动化的单元测试,幸运的是Mock对象可以解决这个问题。

    (这部分没有理解透,这里还使用了easymock)







参考资料:

《单元测试之道Java版:使用JUnit》

Junit使用教程(二)




单元测试——junit