java单元测试总结

java单元测试(使用junit)

http://www.cnblogs.com/feiweiwei/archive/2009/06/16/1024623.html
    JUnit是由 Erich Gamma 和 Kent Beck 编写的一个回归测试框架(regression testing framework),供Java开发人员编写单元测试之用。 


1、概述 
  Junit测试是程序员测试,即所谓白盒测试,因为程序员知道被测试的软件如何(How)完成功能和完成什么样(What)的功能。 
  Junit本质上是一套框架,即开发者制定了一套条条框框,遵循这此条条框框要求编写测试代码,如继承某个类,实现某个接口,就可以用Junit进行自动测试了。 
  由于Junit相对独立于所编写的代码,可以测试代码的编写可以先于实现代码的编写,XP 中推崇的 test first design的实现有了现成的手段:用Junit写测试代码,写实现代码,运行测试,测试失败,修改实现代码,再运行测试,直到测试成功。以后对代码的修改和优化,运行测试成功,则修改成功。 
  Java 下的 team 开发,采用 cvs(版本控制) + ant(项目管理) + junit(集成测试) 的模式时,通过对ant的配置,可以很简单地实现测试自动化。 


  对不同性质的被测对象,如Class,Jsp,Servlet,Ejb等,Junit有不同的使用技巧,以后慢慢地分别讲叙。以下以Class测试为例讲解,除非特殊说明。 


2、下载安装 


去Junit主页下载最新版本3.8.1程序包junit-3.8.1.zip 


用winzip或unzip将junit-3.8.1.zip解压缩到某一目录名为$JUNITHOME 


将junit.jar和$JUNITHOME/junit加入到CLASSPATH中,加入后者只因为测试例程在那个目录下。 


注意不要将junit.jar放在jdk的extension目录下 


运行命令,结果如右图。 
java junit.swingui.TestRunner junit.samples.AllTests 


3、Junit架构 
  下面以Money这个类为例进行说明。 


public class Money { 
private int fAmount;//余额 
private String fCurrency;//货币类型 


public Money(int amount, String currency) { 
fAmount= amount; 
fCurrency= currency; 



public int amount() { 
return fAmount; 



public String currency() { 
return fCurrency; 



public Money add(Money m) {//加钱 
return new Money(amount()+m.amount(), currency()); 



public boolean equals(Object anObject) {//判断钱数是否相等 
if (anObject instanceof Money) { 
Money aMoney= (Money)anObject; 
return aMoney.currency().equals(currency()) 
&& amount() == aMoney.amount(); 

return false; 




  Junit本身是围绕着两个设计模式来设计的:命令模式和集成模式. 


命令模式 
  利用TestCase定义一个子类,在这个子类中生成一个被测试的对象,编写代码检测某个方法被调用后对象的状态与预期的状态是否一致,进而断言程序代码有没有bug。 
  当这个子类要测试不只一个方法的实现代码时,可以先建立测试基础,让这些测试在同一个基础上运行,一方面可以减少每个测试的初始化,而且可以测试这些不同方法之间的联系。 
  例如,我们要测试Money的Add方法,可以如下: 
public class MoneyTest extends TestCase { //TestCase的子类 
public void testAdd() { //把测试代码放在testAdd中 
Money m12CHF= new Money(12, "CHF"); //本行和下一行进行一些初始化 
Money m14CHF= new Money(14, "CHF"); 
Money expected= new Money(26, "CHF");//预期的结果 
Money result= m12CHF.add(m14CHF); //运行被测试的方法 
Assert.assertTrue(expected.equals(result)); //判断运行结果是否与预期的相同 




  如果测试一下equals方法,用类似的代码,如下: 
public class MoneyTest extends TestCase { //TestCase的子类 
public void testEquals() { //把测试代码放在testEquals中 
Money m12CHF= new Money(12, "CHF"); //本行和下一行进行一些初始化 
Money m14CHF= new Money(14, "CHF"); 


Assert.assertTrue(!m12CHF.equals(null));//进行不同情况的测试 
Assert.assertEquals(m12CHF, m12CHF); 
Assert.assertEquals(m12CHF, new Money(12, "CHF")); // (1) 
Assert.assertTrue(!m12CHF.equals(m14CHF)); 




  当要同时进行测试Add和equals方法时,可以将它们的各自的初始化工作,合并到一起进行,形成测试基础,用setUp初始化,用tearDown清除。如下: 
public class MoneyTest extends TestCase {//TestCase的子类 
private Money f12CHF;//提取公用的对象 
private Money f14CHF; 


protected void setUp() {//初始化公用对象 
f12CHF= new Money(12, "CHF"); 
f14CHF= new Money(14, "CHF"); 

public void testEquals() {//测试equals方法的正确性 
Assert.assertTrue(!f12CHF.equals(null)); 
Assert.assertEquals(f12CHF, f12CHF); 
Assert.assertEquals(f12CHF, new Money(12, "CHF")); 
Assert.assertTrue(!f12CHF.equals(f14CHF)); 



public void testSimpleAdd() {//测试add方法的正确性 
Money expected= new Money(26, "CHF"); 
Money result= f12CHF.add(f14CHF); 
Assert.assertTrue(expected.equals(result)); 




  将以上三个中的任一个TestCase子类代码保存到名为MoneyTest.java的文件里,并在文件首行增加 
import junit.framework.*; 
,都是可以运行的。关于Junit运行的问题很有意思,下面单独说明。 
  上面为解释概念“测试基础(fixture)”,引入了两个对两个方法的测试。命令模式与集成模式的本质区别是,前者一次只运行一个测试。 


集成模式 
  利用TestSuite可以将一个TestCase子类中所有test***()方法包含进来一起运行,还可将TestSuite子类也包含进来,从而行成了一种等级关系。可以把TestSuite视为一个容器,可以盛放TestCase中的test***()方法,它自己也可以嵌套。这种体系架构,非常类似于现实中程序一步步开发一步步集成的现况。 
  对上面的例子,有代码如下: 
public class MoneyTest extends TestCase {//TestCase的子类 
.... 
public static Test suite() {//静态Test 
TestSuite suite= new TestSuite();//生成一个TestSuite 
suite.addTest(new MoneyTest("testEquals")); //加入测试方法 
suite.addTest(new MoneyTest("testSimpleAdd")); 
return suite; 




  从Junit2.0开始,有列简捷的方法: 
public class MoneyTest extends TestCase {//TestCase的子类 
.... 
public static Test suite() {静态Test 
return new TestSuite(MoneyTest.class); //以类为参数 




  TestSuite见嵌套的例子,在后面应用案例中有。    


4、测试代码的运行 
  先说最常用的集成模式。 
  测试代码写好以后,可以相应的类中写main方法,用java命令直接运行;也可以不写main方法,用Junit提供的运行器运行。Junit提供了textui,awtui和swingui三种运行器。 
  以前面第2步中的AllTests运行为例,可有四种: 


java junit.textui.TestRunner junit.samples.AllTests 
java junit.awtui.TestRunner junit.samples.AllTests 
java junit.swingui.TestRunner junit.samples.AllTests 
java junit.samples.AllTests 


  main方法中一般也都是简单地用Runner调用suite(),当没有main时,TestRunner自己以运行的类为参数生成了一个TestSuite. 
   
  对于命令模式的运行,有两种方法。 


静态方法 


TestCase test= new MoneyTest("simple add") { 
public void runTest() { 
testSimpleAdd(); 

}; 


动态方法 


TestCase test= new MoneyTest("testSimpleAdd"); 


  我试了一下,好象有问题,哪位朋友成功了,请指点我一下。确实可以。 


import junit.framework.*; 


public class MoneyTest extends TestCase {//TestCase的子类 
private Money f12CHF;//提取公用的对象 
private Money f14CHF; 
public MoneyTest(String name){ 
super(name); 

protected void setUp() {//初始化公用对象 
f12CHF= new Money(12, "CHF"); 
f14CHF= new Money(14, "CHF"); 

public void testEquals() {//测试equals方法的正确性 
Assert.assertTrue(!f12CHF.equals(null)); 
Assert.assertEquals(f12CHF, f12CHF); 
Assert.assertEquals(f12CHF, new Money(12, "CHF")); 
Assert.assertTrue(!f12CHF.equals(f14CHF)); 



public void testAdd() {//测试add方法的正确性 
Money expected= new Money(26, "CHF"); 
Money result= f12CHF.add(f14CHF); 
Assert.assertTrue(expected.equals(result)); 

// public static void main(String[] args) { 
// TestCase test=new MoneyTest("simple add") { 
// public void runTest() { 
// testAdd(); 
// } 
// }; 
// junit.textui.TestRunner.run(test); 
// } 
public static void main(String[] args) { 
TestCase test=new MoneyTest("testAdd"); 
junit.textui.TestRunner.run(test); 




再给一个静态方法用集成测试的例子: 
public static Test suite() { 
TestSuite suite= new TestSuite(); 
suite.addTest( 
new testCar("getWheels") { 
protected void runTest() { testGetWheels(); } 

); 


suite.addTest( 
new testCar("getSeats") { 
protected void runTest() { testGetSeats(); } 

); 
return suite; 



5、应用案例 


Junit Primer例程,运行如下: 
java com.hedong.JunitLearning.Primer.ShoppingCartTest 


Ant+Junit+Mailto实现自动编译、调试并发送结果的build.xml 


JUnit实施,写得很棒,理解也深刻。例程运行如下: 
java com.hedong.JunitLearning.car.testCarNoJunit 
java junit.swingui.TestRunner com.hedong.JunitLearning.car.testCar 


Junit与log4j结合,阿菜的例程运行: 
cd acai 
ant junit 


6、一些问题 
  有人在实践基础上总结出一些非常有价值的使用技巧,我没有经过一一“测试”,暂列在此。 


不要用TestCase的构造函数初始化Fixture,而要用setUp()和tearDown()方法。 


不要依赖或假定测试运行的顺序,因为JUnit利用Vector保存测试方法。所以不同的平台会按不同的顺序从Vector中取出测试方法。不知3.8中是不是还是如此,不过它提供的例子有一个是指定用VectorSuite的,如果不指定呢? 


避免编写有副作用的TestCase。例如:如果随后的测试依赖于某些特定的交易数据,就不要提交交易数据。简单的回滚就可以了。 


当继承一个测试类时,记得调用父类的setUp()和tearDown()方法。 


将测试代码和工作代码放在一起,一边同步编译和更新。(使用Ant中有支持junit的task.) 


测试类和测试方法应该有一致的命名方案。如在工作类名前加上test从而形成测试类名。 


确保测试与时间无关,不要依赖使用过期的数据进行测试。导致在随后的维护过程中很难重现测试。 


如果你编写的软件面向国际市场,编写测试时要考虑国际化的因素。不要仅用母语的Locale进行测试。 


尽可能地利用JUnit提供地assert/fail方法以及异常处理的方法,可以使代码更为简洁。 


测试要尽可能地小,执行速度快。 


把测试程序建立在与被测对象相同的包中 


在你的原始代码目录中避免测试码出现,可在一个源码镜像目录中放测试码 


在自己的应用程序包中包含一个TestSuite测试类 


7、相关资源下载 
以下jar包,我只是做了打包、编译和调试的工作,供下载学习之用,相关的权利属于原作者。 


可运行例程.jar 


Build.xml 


阿菜的例程 


Junit API 汉译(pdf) 


8、未完成的任务 


httpunit 


cactus 


将Junit用链接池测试 
========

Eclipse中使用JUnit4进行单元测试具体操作

http://blog.csdn.net/youtellido/article/details/49457525
简述


Eclipse使用的版本Eclipse Mars(4.5)
JUnit是Java语言的单元测试框架
junit3与junit4的区别 
在junit3中,如果某个类是测试类,必须将其继承类TestCase,如果某个方法是测试方法,必须让这个方法以testXX开头,如果希望指定某个测试方法运行之前运行某个初始化方法,这个方法的名称必须是setUp,如果希望在某个测试方法运行之后运行某个释放资源的方法,这个方法的名称必须是tearDown。 
在junit4中,一个POJO类就是一个测试类,测试方法通过@Test来标识,初始化方法通过@Before来标识,释放资源的方法通过@After来标识,但是为了让junit4的测试类在junit3中也可以使用,习惯于把初始化方法命名为setUp,释放资源的方法命名为tearDown。Test中的测试方法一般以Test来开始。其中标识为Before注解的方法,每次运行测试类,都会执行标识为@After与@Before的方法。 
4.在junit4中提供了一个Assert的类,这个类中有大量的静态方法进行断言的处理,在junit3中由于继承了TestCase,这个TestCase就可以直接assert,而junit4中需要先引入Assert类。
具体操作


1.在Eclipse中新建一个工程TestUnit,如图 
图1 
2.编写一个测试类Number.Java 
图2


package test;


public class Number {
    public int add(int a,int b){
        return a+b;
    }
    public int min(int a,int b){
        if(a>b){
            return a-b;
        }
        return b-a;
    }
}


3.选中类Number.java,右键New,选择JUnit Test Case,如图 
图3
4.在打开的New JUnit Test Case对话框中,按照如下图所示的方式选择配置,基本保持默认配置,如图 
图4 
5.Next,选择要测试的方法,这里全选,如图 
图5 
6.Finish,弹出添加JUnit4 Library的对话框,OK。 
图6 
7.之后生成NumberTest.java,如图 
图7
NumberTest.java


package test;


import static org.junit.Assert.*;


import org.junit.After;
import org.junit.Before;
import org.junit.Test;


public class NumberTest {


    @Before
    public void setUp() throws Exception {
    }


    @After
    public void tearDown() throws Exception {
    }


    @Test
    public void testAdd() {
        fail("Not yet implemented");
    }


    @Test
    public void testMin() {
        fail("Not yet implemented");
    }


}


以上是默认生成的测试框架,接下来我们就要给测试方法添加代码了。 
8.更改代码如下


package test;


import static org.junit.Assert.*;


import org.junit.After;
import org.junit.Before;
import org.junit.Test;


public class NumberTest {
    private Number num;


    @Before
    public void setUp() throws Exception {
        num=new Number();
    }


    @After
    public void tearDown() throws Exception {
    }


    @Test
    public void testAdd() {
        int res=num.add(2, 3);
        assertEquals(5, res);
    }


    @Test
    public void testMin() {
        int res=num.min(8, 3);
        assertEquals(5, res);
    }


}


9.选中NumberTest.java,右键,Run As–>JUnit Test,得到测试结果,当前结果表示测试通过。如图 
图8
========

Java单元测试(Junit+Mock+代码覆盖率)

http://www.cnblogs.com/AloneSword/p/4109407.html


单元测试是编写测试代码,用来检测特定的、明确的、细颗粒的功能。单元测试并不一定保证程序功能是正确的,更不保证整体业务是准备的。


单元测试不仅仅用来保证当前代码的正确性,更重要的是用来保证代码修复、改进或重构之后的正确性。


一般来说,单元测试任务包括


接口功能测试:用来保证接口功能的正确性。
局部数据结构测试(不常用):用来保证接口中的数据结构是正确的
比如变量有无初始值
变量是否溢出
边界条件测试
变量没有赋值(即为NULL)
变量是数值(或字符)
主要边界:最小值,最大值,无穷大(对于DOUBLE等)
溢出边界(期望异常或拒绝服务):最小值-1,最大值+1
临近边界:最小值+1,最大值-1
变量是字符串
引用“字符变量”的边界
空字符串
对字符串长度应用“数值变量”的边界
变量是集合
空集合
对集合的大小应用“数值变量”的边界
调整次序:升序、降序
变量有规律
比如对于Math.sqrt,给出n^2-1,和n^2+1的边界
所有独立执行通路测试:保证每一条代码,每个分支都经过测试
代码覆盖率
语句覆盖:保证每一个语句都执行到了
判定覆盖(分支覆盖):保证每一个分支都执行到
条件覆盖:保证每一个条件都覆盖到true和false(即if、while中的条件语句)
路径覆盖:保证每一个路径都覆盖到
相关软件
Cobertura:语句覆盖
Emma: Eclipse插件Eclemma
各条错误处理通路测试:保证每一个异常都经过测试
JUNIT
JUnit是Java单元测试框架,已经在Eclipse中默认安装。目前主流的有JUnit3和JUnit4。JUnit3中,测试用例需要继承TestCase类。JUnit4中,测试用例无需继承TestCase类,只需要使用@Test等注解。


Junit3


先看一个Junit3的样例


// 测试java.lang.Math  
// 必须继承TestCase  
public class Junit3TestCase extends TestCase {  
    public Junit3TestCase() {  
        super();  
    }  
    
        // 传入测试用例名称  
    public Junit3TestCase(String name) {  
        super(name);  
    }  
   
        // 在每个Test运行之前运行  
    @Override  
    protected void setUp() throws Exception {  
        System.out.println("Set up");  
    }  
        // 测试方法。  
        // 方法名称必须以test开头,没有参数,无返回值,是公开的,可以抛出异常  
        // 也即类似public void testXXX() throws Exception {}  
    public void testMathPow() {  
        System.out.println("Test Math.pow");  
        Assert.assertEquals(4.0, Math.pow(2.0, 2.0));  
    }  
   
    public void testMathMin() {  
        System.out.println("Test Math.min");  
        Assert.assertEquals(2.0, Math.min(2.0, 4.0));  
    }  
   
        // 在每个Test运行之后运行  
    @Override  
    protected void tearDown() throws Exception {  
        System.out.println("Tear down");  
    }  
}  
 
如果采用默认的TestSuite,则测试方法必须是public void testXXX() [throws Exception] {}的形式,并且不能存在依赖关系,因为测试方法的调用顺序是不可预知的。
上例执行后,控制台会输出


Set up  
Test Math.pow  
Tear down  
Set up  
Test Math.min  
Tear down  
 
从中,可以猜测到,对于每个测试方法,调用的形式是:


testCase.setUp();  
testCase.testXXX();  
testCase.tearDown();     
 
运行测试方法


在Eclipse中,可以直接在类名或测试方法上右击,在弹出的右击菜单中选择Run As -> JUnit Test。
在Mvn中,可以直接通过mvn test命令运行测试用例。
也可以通过Java方式调用,创建一个TestCase实例,然后重载runTest()方法,在其方法内调用测试方法(可以多个)。


TestCase test = new Junit3TestCase("mathPow") {  
        // 重载  
    protected void runTest() throws Throwable {  
        testMathPow();  
    };  
};  
test.run();  
 
更加便捷地,可以在创建TestCase实例时直接传入测试方法名称,JUnit会自动调用此测试方法,如


TestCase test = new Junit3TestCase("testMathPow");  
test.run();  
 
Junit TestSuite


TestSuite是测试用例套件,能够运行过个测试方法。如果不指定TestSuite,会创建一个默认的TestSuite。默认TestSuite会扫描当前内中的所有测试方法,然后运行。
如果不想采用默认的TestSuite,则可以自定义TestSuite。在TestCase中,可以通过静态方法suite()返回自定义的suite。


import junit.framework.Assert;  
import junit.framework.Test;  
import junit.framework.TestCase;  
import junit.framework.TestSuite;  
   
public class Junit3TestCase extends TestCase {  
        //...  
    public static Test suite() {  
        System.out.println("create suite");  
        TestSuite suite = new TestSuite();  
        suite.addTest(new Junit3TestCase("testMathPow"));  
        return suite;  
    }  
}  
 
允许上述方法,控制台输出


写道
create suite
Set up
Test Math.pow
Tear down
 
并且只运行了testMathPow测试方法,而没有运行testMathMin测试方法。通过显式指定测试方法,可以控制测试执行的顺序。


也可以通过Java的方式创建TestSuite,然后调用TestCase,如 


// 先创建TestSuite,再添加测试方法  
TestSuite testSuite = new TestSuite();  
testSuite.addTest(new Junit3TestCase("testMathPow"));  
   
// 或者 传入Class,TestSuite会扫描其中的测试方法。  
TestSuite testSuite = new TestSuite(Junit3TestCase.class,Junit3TestCase2.class,Junit3TestCase3.class);  
   
// 运行testSuite  
TestResult testResult = new TestResult();  
testSuite.run(testResult);  
 
testResult中保存了很多测试数据,包括运行测试方法数目(runCount)等。


JUnit4


与JUnit3不同,JUnit4通过注解的方式来识别测试方法。目前支持的主要注解有:


@BeforeClass 全局只会执行一次,而且是第一个运行
@Before 在测试方法运行之前运行
@Test 测试方法
@After 在测试方法运行之后允许
@AfterClass 全局只会执行一次,而且是最后一个运行
@Ignore 忽略此方法
下面举一个样例:


import org.junit.After;  
import org.junit.AfterClass;  
import org.junit.Assert;  
import org.junit.Before;  
import org.junit.BeforeClass;  
import org.junit.Ignore;  
import org.junit.Test;  
   
public class Junit4TestCase {  
   
    @BeforeClass  
    public static void setUpBeforeClass() {  
        System.out.println("Set up before class");  
    }  
   
    @Before  
    public void setUp() throws Exception {  
        System.out.println("Set up");  
    }  
   
    @Test  
    public void testMathPow() {  
        System.out.println("Test Math.pow");  
        Assert.assertEquals(4.0, Math.pow(2.0, 2.0), 0.0);  
    }  
   
    @Test  
    public void testMathMin() {  
        System.out.println("Test Math.min");  
        Assert.assertEquals(2.0, Math.min(2.0, 4.0), 0.0);  
    }  
   
        // 期望此方法抛出NullPointerException异常  
    @Test(expected = NullPointerException.class)  
    public void testException() {  
        System.out.println("Test exception");  
        Object obj = null;  
        obj.toString();  
    }  
   
        // 忽略此测试方法  
    @Ignore  
    @Test  
    public void testMathMax() {  
          Assert.fail("没有实现");  
    }  
        // 使用“假设”来忽略测试方法  
    @Test  
    public void testAssume(){  
        System.out.println("Test assume");  
                // 当假设失败时,则会停止运行,但这并不会意味测试方法失败。  
        Assume.assumeTrue(false);  
        Assert.fail("没有实现");  
    }  
   
    @After  
    public void tearDown() throws Exception {  
        System.out.println("Tear down");  
    }  
   
    @AfterClass  
    public static void tearDownAfterClass() {  
        System.out.println("Tear down After class");  
    }  
   
}  
 
如果细心的话,会发现Junit3的package是junit.framework,而Junit4是org.junit。
执行此用例后,控制台会输出


写道
Set up before class
Set up
Test Math.pow
Tear down
Set up
Test Math.min
Tear down
Set up
Test exception
Tear down
Set up
Test assume
Tear down
Tear down After class
 
可以看到,执行次序是@BeforeClass -> @Before -> @Test -> @After -> @Before -> @Test -> @After -> @AfterClass。@Ignore会被忽略。


运行测试方法


与Junit3类似,可以在Eclipse中运行,也可以通过mvn test命令运行。


Assert


Junit3和Junit4都提供了一个Assert类(虽然package不同,但是大致差不多)。Assert类中定义了很多静态方法来进行断言。列表如下:


assertTrue(String message, boolean condition) 要求condition == true
assertFalse(String message, boolean condition) 要求condition == false
fail(String message) 必然失败,同样要求代码不可达
assertEquals(String message, XXX expected,XXX actual) 要求expected.equals(actual)
assertArrayEquals(String message, XXX[] expecteds,XXX [] actuals) 要求expected.equalsArray(actual)
assertNotNull(String message, Object object) 要求object!=null
assertNull(String message, Object object) 要求object==null
assertSame(String message, Object expected, Object actual) 要求expected == actual
assertNotSame(String message, Object unexpected,Object actual) 要求expected != actual
assertThat(String reason, T actual, Matcher matcher) 要求matcher.matches(actual) == true
Mock/Stub
Mock和Stub是两种测试代码功能的方法。Mock测重于对功能的模拟。Stub测重于对功能的测试重现。比如对于List接口,Mock会直接对List进行模拟,而Stub会新建一个实现了List的TestList,在其中编写测试的代码。
强烈建议优先选择Mock方式,因为Mock方式下,模拟代码与测试代码放在一起,易读性好,而且扩展性、灵活性都比Stub好。
比较流行的Mock有:


JMock
EasyMock
Mockito
powermock
其中EasyMock和Mockito对于Java接口使用接口代理的方式来模拟,对于Java类使用继承的方式来模拟(也即会创建一个新的Class类)。Mockito支持spy方式,可以对实例进行模拟。但它们都不能对静态方法和final类进行模拟,powermock通过修改字节码来支持了此功能。


EasyMock


IBM上有几篇介绍EasyMock使用方法和原理的文章:EasyMock 使用方法与原理剖析,使用 EasyMock 更轻松地进行测试。
EasyMock把测试过程分为三步:录制、运行测试代码、验证期望。
录制过程大概就是:期望method(params)执行times次(默认一次),返回result(可选),抛出exception异常(可选)。
验证期望过程将会检查方法的调用次数。
一个简单的样例是: 


@Test  
public void testListInEasyMock() {  
    List list = EasyMock.createMock(List.class);  
    // 录制过程  
   
    // 期望方法list.set(0,1)执行2次,返回null,不抛出异常  
    expect1: EasyMock.expect(list.set(0, 1)).andReturn(null).times(2);  
    // 期望方法list.set(0,1)执行1次,返回null,不抛出异常  
    expect2: EasyMock.expect(list.set(0, 1)).andReturn(1);  
   
    // 执行测试代码  
    EasyMock.replay(list);  
        // 执行list.set(0,1),匹配expect1期望,会返回null  
    Assert.assertNull(list.set(0, 1));  
        // 执行list.set(0,1),匹配expect1(因为expect1期望执行此方法2次),会返回null  
    Assert.assertNull(list.set(0, 1));  
        // 执行list.set(0,1),匹配expect2,会返回1  
    Assert.assertEquals(1, list.set(0, 1));  
   
    // 验证期望  
    EasyMock.verify(list);  
}  
 
EasyMock还支持严格的检查,要求执行的方法次序与期望的完全一致。


Mockito


Mockito是Google Code上的一个开源项目,Api相对于EasyMock更好友好。与EasyMock不同的是,Mockito没有录制过程,只需要在“运行测试代码”之前对接口进行Stub,也即设置方法的返回值或抛出的异常,然后直接运行测试代码,运行期间调用Mock的方法,会返回预先设置的返回值或抛出异常,最后再对测试代码进行验证。可以查看此文章了解两者的不同。
官方提供了很多样例,基本上包括了所有功能,可以去看看。
这里从官方样例中摘录几个典型的:


验证调用行为
import static org.mockito.Mockito.*;  
   
//创建Mock  
List mockedList = mock(List.class);  
   
//使用Mock对象  
mockedList.add("one");  
mockedList.clear();  
   
//验证行为  
verify(mockedList).add("one");  
verify(mockedList).clear();  
 
 
对Mock对象进行Stub
//也可以Mock具体的类,而不仅仅是接口  
LinkedList mockedList = mock(LinkedList.class);  
   
//Stub  
when(mockedList.get(0)).thenReturn("first"); // 设置返回值  
when(mockedList.get(1)).thenThrow(new RuntimeException()); // 抛出异常  
   
//第一个会打印 "first"  
System.out.println(mockedList.get(0));  
   
//接下来会抛出runtime异常  
System.out.println(mockedList.get(1));  
   
//接下来会打印"null",这是因为没有stub get(999)  
System.out.println(mockedList.get(999));  
    
// 可以选择性地验证行为,比如只关心是否调用过get(0),而不关心是否调用过get(1)  
verify(mockedList).get(0);   
 
代码覆盖率
比较流行的工具是Emma和Jacoco,Ecliplse插件有eclemma。eclemma2.0之前采用的是Emma,之后采用的是Jacoco。这里主要介绍一下Jacoco。Eclmama由于是Eclipse插件,所以非常易用,就不多做介绍了。


Jacoco


Jacoco可以嵌入到Ant、Maven中,也可以使用Java Agent技术监控任意Java程序,也可以使用Java Api来定制功能。
Jacoco会监控JVM中的调用,生成监控结果(默认保存在jacoco.exec文件中),然后分析此结果,配合源代码生成覆盖率报告。需要注意的是:监控和分析这两步,必须使用相同的Class文件,否则由于Class不同,而无法定位到具体的方法,导致覆盖率均为0%。


Java Agent嵌入


首先,需要下载jacocoagent.jar文件,然后在Java程序启动参数后面加上 -javaagent:[yourpath/]jacocoagent.jar=[option1]=[value1],[option2]=[value2],具体的options可以在此页面找到。默认会在JVM关闭时(注意不能是kill -9),输出监控结果到jacoco.exec文件中,也可以通过socket来实时地输出监控报告(可以在Example代码中找到简单实现)。


Java Report


可以使用Ant、Mvn或Eclipse来分析jacoco.exec文件,也可以通过API来分析。


public void createReport() throws Exception {  
            // 读取监控结果  
    final FileInputStream fis = new FileInputStream(new File("jacoco.exec"));  
    final ExecutionDataReader executionDataReader = new ExecutionDataReader(fis);  
            // 执行数据信息  
    ExecutionDataStore executionDataStore = new ExecutionDataStore();  
            // 会话信息  
    SessionInfoStore sessionInfoStore = new SessionInfoStore();  
   
    executionDataReader.setExecutionDataVisitor(executionDataStore);  
    executionDataReader.setSessionInfoVisitor(sessionInfoStore);  
   
    while (executionDataReader.read()) {  
    }  
   
    fis.close();  
               
            // 分析结构  
            final CoverageBuilder coverageBuilder = new CoverageBuilder();  
    final Analyzer analyzer = new Analyzer(executionDataStore, coverageBuilder);  
   
            // 传入监控时的Class文件目录,注意必须与监控时的一样  
    File classesDirectory = new File("classes");  
    analyzer.analyzeAll(classesDirectory);  
   
    IBundleCoverage bundleCoverage = coverageBuilder.getBundle("Title");  
            // 输出报告  
        File reportDirectory = new File("report"); // 报告所在的目录  
    final HTMLFormatter htmlFormatter = new HTMLFormatter();  // HTML格式  
    final IReportVisitor visitor = htmlFormatter.createVisitor(new FileMultiReportOutput(reportDirectory));  
            // 必须先调用visitInfo  
    visitor.visitInfo(sessionInfoStore.getInfos(), executionDataStore.getContents());  
    File sourceDirectory = new File("src"); // 源代码目录  
            // 遍历所有的源代码  
            // 如果不执行此过程,则在报告中只能看到方法名,但是无法查看具体的覆盖(因为没有源代码页面)  
    visitor.visitBundle(bundleCoverage, new DirectorySourceFileLocator(sourceDirectory, "utf-8", 4));  
            // 执行完毕  
    visitor.visitEnd();  
}   
========

菜鸟学Java(二十一)——如何更好的进行单元测试——JUnit



测试在软件生命周期中的重要性,不用我多说想必大家也都非常清楚。软件测试有很多分类,从测试的方法上可分为:黑盒测试、白盒测试、静态测试、动态测试等;从软件开发的过程分为:单元测试、集成测试、确认测试、验收、回归等。


在众多的分类中,与开发人员关系最紧密的莫过于单元测试了。像其他种类的测试基本上都是由专门的测试人员来完成,只有单元测试是完全由开发人员来完成的。那么今天我们就来说说什么是单元测试,为什么要进行单元测试,以及如更好的何进行单元测试。


什么是单元测试?
单元测试(unit testing),是指对软件中的最小可测试单元进行检查和验证。比如我们可以测试一个类,或者一个类中的一个方法。


为什么要进行单元测试?
为什么要进行单元测试?说白了就是单元测试有什么好处,其实测试的好处无非就是减少bug、提高代码质量、使代码易于维护等。单元测试有什么好处请看一下百度百科中归纳的四条:


1、它是一种验证行为。
程序中的每一项功能都是测试来验证它的正确性。它为以后的开发提供支援。就算是开发后期,我们也可以轻松的增加功能或更改程序结构,而不用担心这个过程中会破坏重要的东西。而且它为代码的重构提供了保障。这样,我们就可以更自由的对程序进行改进。


2、它是一种设计行为。
编写单元测试将使我们从调用者观察、思考。特别是先写测试(test-first),迫使我们把程序设计成易于调用和可测试的,即迫使我们解除软件中的耦合。


3、它是一种编写文档的行为。
单元测试是一种无价的文档,它是展示函数或类如何使用的最佳文档。这份文档是可编译、可运行的,并且它保持最新,永远与代码同步。


4、它具有回归性。
自动化的单元测试避免了代码出现回归,编写完成之后,可以随时随地的快速运行测试。


如何更好的进行单元测试?


在讨论如何更好的进行单元测试之前,先来看看我们以前是怎么测试代码的。
以前是这样测试程序的:
[java] view plain copy 在CODE上查看代码片派生到我的代码片
public int add(int x,int y) {  
    return x + y;  
}  
      
public static void main(String args[]) {  
    int z = new Junit().add(2, 3);  
    System.out.println(z);  
}  


如上面所示,在测试我们写好的一个方法时,通常是用一个main方法调用一下我们要测试的方法,然后将结果打印一下。现在看来这种方式已经非常out了,所以出现了很多单元测试的工具,如:JUnit、TestNG等。借助它们可以让我们的单元测试变得非常方便、高效。今天就说说如何利用JUnit进行单元测试。


我们新建一个Java Project以便进行演示,至于Java Project怎么创建我就不在此赘述了,如果连怎么建Java Project,那你还不适合看这篇文章。建好以后在该项目的“src”目录上右击,选择new——》JUnit Test Case,然后按下图填写必要信息:


填写好包名和类名(选择New JUnit 4 Test),点击最下面的那个“Browse”按钮来选择需要测试的类:


手动输入我们要测试的类,选择该类,点击“OK”,回到第一张图的界面,然后点击“Next”,来到下图:


勾选要测试的方法,点击“Finish”,这样我们的JUnit测试实例就建好了。然后就可以写具体的测试了:


package com.tgb.junit.test;  
  
//静态引入  
import static org.junit.Assert.*;  
import static org.hamcrest.Matchers.*;  
  
import org.junit.Test;  
  
import com.tgb.junit.Junit;  
  
public class JUnitTest {  
  
    @Test  
    public void testAdd() {  
        int z = new  Junit().add(2, 3);  
        assertThat(z , is(5));  
    }  
  
    @Test  
    public void testDivide() {  
        int z = new Junit().divide(4, 2);         
        assertThat(z, is(2));  
    }  
}  


写好以后,右击该类选择“Run As”——》“JUnit Test”,出现下图代表测试通过:


到这里,可能有人会有疑问,JUnit跟用main方法测试有什么区别呢?
首先,JUnit的结果更加直观,直接根据状态条的颜色即可判断测试是否通过,而用main方法你需要去检查他的输出结果,然后跟自己的期望结果进行对比,才能知道是否测试通过。有一句话能够很直观的说明这一点——keeps the bar green to keeps the code clean。意思就是说,只要状态条是绿色的,那么你的代码就是正确的。
第二点,JUnit让我们同时运行多个测试变得非常方便,下面就演示一下如何进行多实例测试:
首先我们要再建一个待测试类,然后再建一个对应的JUnit测试实例,步骤略。然后在我们测试实例的包上右击选择“Run As”——》“Run Configurations”,如下图;


选择第二项“Run all tests in the selected project, package or source folder”,然后点击“Run”效果如下:


可以看到,我们本次测试了两个类,共三个方法,这种方便的效果在测试实例越多的情况下,体现的越明显。至于main方法运行多个测试,想想就觉得非常麻烦,这里就不演示了。


JUnit除了可以测试这些简单的小程序,还可以测试Struts、JDBC等等,这里只是用这个小程序做过简单的介绍。本实例使用的是hamcrest断言,而没有使用老的断言,因为hamcrest断言更加接近自然语言的表达方式,更易于理解。


本实例需要引入以下三个jar包:
hamcrest-core-1.3.jar
hamcrest-library-1.3.jar
junit-4.10.jar


最后附上常用hamcrest断言的使用说明:
[java] view plain copy 在CODE上查看代码片派生到我的代码片
数值类型  
//n大于1并且小于15,则测试通过  
assertThat( n, allOf( greaterThan(1), lessThan(15) ) );  
//n大于16或小于8,则测试通过  
assertThat( n, anyOf( greaterThan(16), lessThan(8) ) );  
//n为任何值,都测试通过  
assertThat( n, anything() );  
//d与3.0的差在±0.3之间,则测试通过  
assertThat( d, closeTo( 3.0, 0.3 ) );  
//d大于等于5.0,则测试通过  
assertThat( d, greaterThanOrEqualTo (5.0) );  
//d小于等于16.0,则测试通过  
assertThat( d, lessThanOrEqualTo (16.0) );  
  
字符类型  
//str的值为“tgb”,则测试通过  
assertThat( str, is( "tgb" ) );  
//str的值不是“tgb”,则测试通过  
assertThat( str, not( "tgb" ) );  
//str的值包含“tgb”,则测试通过  
assertThat( str, containsString( "tgb" ) );  
//str以“tgb”结尾,则测试通过  
assertThat( str, endsWith("tgb" ) );   
//str以“tgb”开头,则测试通过  
assertThat( str, startsWith( "tgb" ) );   
//str忽略大小写后,值为“tgb”,则测试通过  
assertThat( str, equalToIgnoringCase( "tgb" ) );   
//str忽略空格后,值为“tgb”,则测试通过  
assertThat( str, equalToIgnoringWhiteSpace( "tgb" ) );  
//n与nExpected相等,则测试通过(对象之间)  
assertThat( n, equalTo( nExpected ) );   
  
collection类型  
//map中包含key和value为“tgb”的键值对,则测试通过  
assertThat( map, hasEntry( "tgb", "tgb" ) );  
//list中包含“tgb”元素,则测试通过  
assertThat( iterable, hasItem ( "tgb" ) );  
//map中包含key为“tgb”的元素,则测试通过  
assertThat( map, hasKey ( "tgb" ) );  
//map中包含value为“tgb”的元素,则测试通过  
assertThat( map, hasValue ( "tgb" ) );  
========

java编程之单元测试(Junit)实例分析(附实例源码)

http://www.jb51.net/article/74976.htm


这篇文章主要介绍了java编程之单元测试(Junit),结合实例形式较为详细的分析总结了Java单元测试的原理、步骤及相关注意事项,并附带了完整代码供读者下载参考,需要的朋友可以参考下
..本文实例讲述了java编程之单元测试。分享给大家供大家参考,具体如下:


完整实例代码代码点击此处本站下载。


在有些时候,我们需要对我们自己编写的代码进行单元测试(好处是,减少后期维护的精力和费用),这是一些最基本的模块测试。当然,在进行单元测试的同时也必然得清楚我们测试的代码的内部逻辑实现,这样在测试的时候才能清楚地将我们希望代码逻辑实现得到的结果和测试实际得到的结果进行验证对比。


废话少说,上代码:


首先创建一个java工程,在工程中创建一个被单元测试的Student数据类,如下:
package com.phicomme.hu;  public class Student  {   private String name;   private String sex;   private int high;   private int age;   private String school;   public Student(String name, String sex ,int high, int age, String school)   {    this.name = name;    this.sex = sex;    this.high = high;    this.age = age;    this.school = school;   }   public String getName()   {    return name;   }   public void setName(String name)   {    this.name = name;   }   public String getSex()   {    return sex;   }   public void setSex(String sex)   {    this.sex = sex;   }   public int getHigh()   {    return high;   }   public void setHigh(int high)   {    this.high = high;   }   public int getAge()   {    return age;   }   public boolean setAge(int age)   {    if (age >25)    {     return false;    }    else   {     this.age = age;     return true;    }       }   public String getSchool()   {    return school;   }   public void setSchool(String school)   {    this.school = school;   }  } 
在eclipse下单元测试这个类:


首先导入Junit包:选中java工程,点击鼠标右键--->选择properties---->在窗口中选Java Build Path---->在右侧点击Add Library---->在弹出的窗口列表中选中Junit---->下一步----->Junit 4(我用的是Junit 4)---->finish


这样Junit 4包就导完了,接下来就是创建测试类:


将测试类和被测试类放在不同的包中(也可以放在同一个包中,此处只是为了区别),代码如下:


测试类1:


package com.phicomme.test;  import com.phicomme.hu.Student;  import junit.framework.TestCase;  public class StudentTest01 extends TestCase  {   Student testStudent;   //此方法在执行每一个测试方法之前(测试用例)之前调用   @Override  protected void setUp() throws Exception   {    // TODO Auto-generated method stub    super.setUp();    testStudent = new Student("djm", "boy", 178, 24, "华东政法");    System.out.println("setUp()");   }   //此方法在执行每一个测试方法之后调用   @Override  protected void tearDown() throws Exception   {    // TODO Auto-generated method stub    super.tearDown();    System.out.println("tearDown()");   }   //测试用例,测试Person对象的getSex()方法   public void testGetSex()   {    assertEquals("boy", testStudent.getSex());    System.out.println("testGetSex()");   }   //测试Person对象的getAge()方法   public void testGetAge()   {    assertEquals(24, testStudent.getAge());    System.out.println("testGetAge()");   }  } 
测试类2:


package com.phicomme.test;  import junit.framework.TestCase;  import com.phicomme.hu.Student;  public class StudentTest extends TestCase  {   private Student testStudent;   @Override  protected void setUp() throws Exception   {    // TODO Auto-generated method stub    super.setUp();    testStudent = new Student("steven_hu", "boy", 170 , 23, "上海理工");   }   @Override  protected void tearDown() throws Exception   {    // TODO Auto-generated method stub    super.tearDown();   }   public void testSetage()   {    assertTrue(testStudent.setAge(21));   }   public void testGetSchool()   {    //预期值和实际值不一样,测试时出现失败(Failure)    assertEquals("南昌大学", testStudent.getSchool());   }   public void testGetName()   {    assertEquals("hdy", testStudent.getName());   }  } 
当然,如果同时需要一起测试以上这两个测试类,可以通过TestSuite类实现,它相当于是一个套件,可以把所有测试类添进来一起运行测试;


代码如下:


package com.phicomme.test;  import com.phicomme.hu.StudentTest02;  import junit.framework.Test;  import junit.framework.TestSuite;  public class AllTest  {   //static PersonTest p = new PersonTest();   //static PersonTest p1 = new PersonTest();   public static Test suite()   {    TestSuite suite = new TestSuite("Test for com.phicomme.test");    //suite.addTest(p);    //suite.addTest(p1);    suite.addTestSuite(StudentTest.class);    suite.addTestSuite(StudentTest01.class);    return suite;   }  } 
最后,分别测试以上三个类(选中需要测试的类---->鼠标右键---->Run As---->Junit Test):


StudentTest类的测试结果图:


StudentTest01类的测试结果图:


AllTest类的测试结果图:


有关java的测试就讲到这里,希望对大家有帮助,有时间也会接着讲讲有关android的单元测试,和在手机上实现编写一个UI界面替代eclipse如上图中的测试界面;


========

你可能感兴趣的:(转载,Java,junit,单元测试,java)