`
gelongmei
  • 浏览: 195681 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
文章分类
社区版块
存档分类
最新评论

使用JMockit编写java单元测试

 
阅读更多
使用JMockit编写java单元测试
http://blog.csdn.net/chjttony/article/details/17838693

之前《有效使用Mock编写java单元测试》一文中层介绍过使用EasyMock和PowerMock来编写java单元测试,今天介绍一个更加强大的工具——JMockit。
引用单元测试中mock的使用及mock神器jmockit实践中的java单元测试中各种Mock框架对比,就能明白JMockit有多么强大:

JMockit是基于JavaSE5中的java.lang.instrument包开发,内部使用ASM库来动态修改java的字节码,使得java这种静态语言可以想动态脚本语言一样动态设置被Mock对象私有属性,模拟静态、私有方法行为等等,对于手机开发,嵌入式开发等要求代码尽量简洁的情况下,或者对于被测试代码不想做任何修改的前提下,使用JMockit可以轻松搞定很多测试场景。
通过如下方式在maven中添加JMockit的相关依赖:
[html] view plaincopy在CODE上查看代码片派生到我的代码片
<dependency> 
            <groupId>com.googlecode.jmockit</groupId> 
            <artifactId>jmockit</artifactId> 
            <version>1.5</version> 
            <scope>test</scope> 
        </dependency> 
        <dependency> 
            <groupId>com.googlecode.jmockit</groupId> 
            <artifactId>jmockit-coverage</artifactId> 
            <version>0.999.24</version> 
            <scope>test</scope> 
        </dependency> 
JMockit有两种Mock方式:基于行为的Mock方式和基于状态的Mock方式:
引用单元测试中mock的使用及mock神器jmockit实践中JMockit API和工具如下:

(1).基于行为的Mock方式:
非常类似与EasyMock和PowerMock的工作原理,基本步骤为:
1.录制方法预期行为。
2.真实调用。
3.验证录制的行为被调用。
通过一个简单的例子来介绍JMockit的基本流程:
要Mock测试的方法如下:
[java] view plaincopy在CODE上查看代码片派生到我的代码片
public class MyObject { 
    public String hello(String name){ 
        return "Hello " + name; 
    } 

使用JMockit编写的单元测试如下:
[java] view plaincopy在CODE上查看代码片派生到我的代码片
@Mocked  //用@Mocked标注的对象,不需要赋值,jmockit自动mock 
MyObject obj; 
 
@Test 
public void testHello() { 
    new NonStrictExpectations() {//录制预期模拟行为 
        { 
            obj.hello("Zhangsan"); 
            returns("Hello Zhangsan"); 
            //也可以使用:result = "Hello Zhangsan"; 
        } 
    }; 
    assertEquals("Hello Zhangsan", obj.hello("Zhangsan"));//调用测试方法 
    new Verifications() {//验证预期Mock行为被调用 
        { 
            obj.hello("Hello Zhangsan"); 
            times = 1; 
        } 
    }; 

JMockit也可以分类为非局部模拟与局部模拟,区分在于Expectations块是否有参数,有参数的是局部模拟,反之是非局部模拟。
而Expectations块一般由Expectations类和NonStrictExpectations类定义,类似于EasyMock和PowerMock中的Strict Mock和一般性Mock。
用Expectations类定义的,则mock对象在运行时只能按照 Expectations块中定义的顺序依次调用方法,不能多调用也不能少调用,所以可以省略掉Verifications块;
而用NonStrictExpectations类定义的,则没有这些限制,所以如果需要验证,则要添加Verifications块。
上述的例子使用了非局部模拟,下面我们使用局部模拟来改写上面的测试,代码如下:
[java] view plaincopy在CODE上查看代码片派生到我的代码片
@Test 
public void testHello() { 
    final MyObject obj = new MyObject(); 
    new NonStrictExpectations(obj) {//录制预期模拟行为 
        { 
            obj.hello("Zhangsan"); 
            returns("Hello Zhangsan"); 
            //也可以使用:result = "Hello Zhangsan"; 
        } 
    }; 
    assertEquals("Hello Zhangsan", obj.hello("Zhangsan"));//调用测试方法 
    new Verifications() {//验证预期Mock行为被调用 
        { 
            obj.hello("Hello Zhangsan"); 
            times = 1; 
        } 
    }; 

模拟静态方法:
[java] view plaincopy在CODE上查看代码片派生到我的代码片
@Test 
public void testMockStaticMethod() { 
    new NonStrictExpectations(ClassMocked.class) { 
        { 
            ClassMocked.getDouble(1);//也可以使用参数匹配:ClassMocked.getDouble(anyDouble); 
            result = 3; 
        } 
    }; 
 
    assertEquals(3, ClassMocked.getDouble(1)); 
 
    new Verifications() { 
        { 
            ClassMocked.getDouble(1); 
            times = 1; 
        } 
    }; 

模拟私有方法:
如果ClassMocked类中的getTripleString(int)方法指定调用一个私有的multiply3(int)的方法,我们可以使用如下方式来Mock:
[java] view plaincopy在CODE上查看代码片派生到我的代码片
@Test 
public void testMockPrivateMethod() throws Exception { 
    final ClassMocked obj = new ClassMocked(); 
    new NonStrictExpectations(obj) { 
        { 
            this.invoke(obj, "multiply3", 1);//如果私有方法是静态的,可以使用:this.invoke(null, "multiply3") 
            result = 4; 
        } 
    }; 
 
    String actual = obj.getTripleString(1); 
    assertEquals("4", actual); 
 
    new Verifications() { 
        { 
            this.invoke(obj, "multiply3", 1); 
            times = 1; 
        } 
    }; 

设置Mock对象私有属性的值:
我们知道EasyMock和PowerMock的Mock对象是通过JDK/CGLIB动态代理实现的,本质上是类的继承或者接口的实现,但是在java面向对象编程中,基类对象中的私有属性是无法被子类继承的,所以如果被Mock对象的方法中使用到了其自身的私有属性,并且这些私有属性没有提供对象访问方法,则使用传统的Mock方法是无法进行测试的,JMockit提供了设置Mocked对象私有属性值的方法,代码如下:
被测试代码:
[java] view plaincopy在CODE上查看代码片派生到我的代码片
public class ClassMocked { 
    private String name = "name_init"; 
 
    public String getName() { 
        return name; 
    } 
     
    private static String className="Class3Mocked_init"; 
     
    public static String getClassName(){ 
        return className; 
    } 

使用JMockit设置私有属性:
[java] view plaincopy在CODE上查看代码片派生到我的代码片
@Test 
public void testMockPrivateProperty() throws IOException { 
    final ClassMocked obj = new ClassMocked(); 
    new NonStrictExpectations(obj) { 
        { 
            this.setField(obj, "name", "name has bean change!"); 
        } 
    }; 
 
    assertEquals("name has bean change!", obj.getName()); 

使用JMockit设置静态私有属性:
[java] view plaincopy在CODE上查看代码片派生到我的代码片
@Test 
public void testMockPrivateStaticProperty() throws IOException { 
    new NonStrictExpectations(Class3Mocked.class) { 
        { 
            this.setField(ClassMocked.class, "className", "className has bean change!"); 
        } 
    }; 
 
    assertEquals("className has bean change!", ClassMocked.getClassName()); 

(2).基于状态的Mock方式:
JMockit上面的基于行为Mock方式和传统的EasyMock和PowerMock流程基本类似,相当于把被模拟的方法当作黑盒来处理,而JMockit的基于状态的Mock可以直接改写被模拟方法的内部逻辑,更像是真正意义上的白盒测试,下面通过简单例子介绍JMockit基于状态的Mock。
被测试的代码如下:
[java] view plaincopy在CODE上查看代码片派生到我的代码片
public class StateMocked { 
     
    public static int getDouble(int i){ 
        return i*2; 
    } 
     
    public int getTriple(int i){ 
        return i*3; 
    } 

改写普通方法内容:
[java] view plaincopy在CODE上查看代码片派生到我的代码片
@Test 
public void testMockNormalMethodContent() throws IOException { 
    StateMocked obj = new StateMocked(); 
    new MockUp<StateMocked>() {//使用MockUp修改被测试方法内部逻辑 
        @Mock 
      public int getTriple(int i) { 
            return i * 30; 
        } 
    }; 
    assertEquals(30, obj.getTriple(1)); 
    assertEquals(60, obj.getTriple(2)); 
    Mockit.tearDownMocks();//注意:在JMockit1.5之后已经没有Mockit这个类,使用MockUp代替,mockUp和tearDown方法在MockUp类中 

修改静态方法的内容:
基于状态的JMockit改写静态/final方法内容和测试普通方法没有什么区别,需要注意的是在MockUp中的方法除了不包含static关键字以外,其他都和被Mock的方法签名相同,并且使用@Mock标注,测试代码如下:
[java] view plaincopy在CODE上查看代码片派生到我的代码片
@Test 
    public void testGetTriple() { 
        new MockUp<StateMocked>() { 
            @Mock   
            public int getDouble(int i){   
                return i*20;   
            } 
        };   
        assertEquals(20, StateMocked.getDouble(1));   
        assertEquals(40, StateMocked.getDouble(2));  
    } 
JMockit和PowerMock混用时不兼容问题:
由于PowerMock需要在单元测试类上添加@RunWith(PowerMockRunner.class)注解,用于表面使用PowerMock来执行单元测试,而JMockit不需要指定@RunWith注解,因此当一个单元测试类中混合使用PowerMock和JMockit时,JMockit总是会报错初始化失败,因此我建议不要在同一个单元测试类中混用PowerMock和JMockit。
另外,JMockit要求必须出现在JVM classpath的中Junit前面位置,因此在添加Maven依赖时记得要把JMockit放在Junit最前面,否则同样报错JMockit初始化失败。

统计JMockit的单元测试覆盖率:
由于JMockit使用JavaSE5中的java.lang.instrument包开发,因此一般的单元测试覆盖率统计插件和工具对其无法工作,必须要借助自带的JMockit coverage才行,对于使用Eclemma插件和maven+sonar方式的单元测试覆盖率统计,分别有如下方法:
(1).Eclemma插件方式:
如果直接使用Eclemma插件来统计单元测试覆盖率,会发现Eclemma长时间挂起阻塞,强行结束时会报错说找不到-javaagent等等,解决方法如下:
在Eclipse中右键选择Coverage as ->Coverage Configurations,配置Junit的JVM参数如下:
-javaagent:"${settings.localRepository}"/com/googlecode/jmockit/jmockit-coverage/0.999.24/jmockit-coverage-0.999.24.jar
其中${settings.localRepository}是maven资源目录,例如:
-javaagent:D:\userdata\administrator\.m2\repository\com\googlecode\jmockit\jmockitcoverage\0.999.24\jmockit-coverage-0.999.24.jar
(2).Maven+Sonar方式:
如果直接使用mvn sonar:sonar命令时,发现任何单元测试无法执行,长时间卡住,强行结束后再次执行时maven工程target目录下surefire目录无法删除,只有重启机器后才能删除,解决方法如下:
在pom文件中添加如下的插件:
[html] view plaincopy在CODE上查看代码片派生到我的代码片
<plugin> 
                <groupId>org.apache.maven.plugins</groupId> 
                <artifactId>maven-surefire-plugin</artifactId> 
                <version>2.12</version> 
                <configuration> 
                    <argLine>-javaagent:"${settings.localRepository}"/com/googlecode/jmockit/jmockit-coverage/0.999.24/jmockit-coverage-0.999.24.jar</argLine> 
                </configuration> 
            </plugin> 
再次执行mvn sonar:sonar命令就可以正常统计出JMockit的单元测试覆盖率。
更多内容请参考JMockit官方文档:http://jmockit.googlecode.com/svn/trunk/www/tutorial.html。
分享到:
评论

相关推荐

    JMOCKIT mock 静态方法 单元测试

    可以用来Mock JAVA代码中的静态方法

    JMockit教程.pdf

    开源社区有许多优秀的Java单元测试框架,JMockit是其中之一。正如其名,在单元测试时,CUT(Code Under Test ,被测试代码)可能会依赖其他代码,可以使用JMockit API来mock被依赖的代码,从而进行隔离测试。 ...

    jutf:Java单元测试框架(Warp H2Mockitojmockit工具,使Java应用程序更好)

    Java单元测试框架(Warp H2 / Mockito / jmockit工具使Java应用程序更好) 功能 jutf(无弹簧依赖版本) 使用mockito / jmockit来模拟界面 实用程序模拟get / set / construct / tostring H2内存测试数据库工具 ...

    jmockit

    jmockit

    jmockit jmockit jmockit jmockit

    jmockit jmockit jmockit jmockit jmockit jmockit

    Jmockit用法示例

    Jmockit用法示例,对各种函数(公有、私有、静态、final、无参数、带参数、接口)进行mock的方法

    jmockit框架所使用jar包

    jmockit框架所使用jar包,在写单元测试的过程中我们会发现需要测试的类有很多依赖,这些依赖的类或者资源又会有依赖,导致在单元测试代码里无法完成构建,我们应对的方法是Mock。简单的说就是模拟这些需要构建的类...

    jmockit_sample:jmockit 使用示例代码和说明

    使用mock测试的几点好处: a、能够帮助我们解耦设计,即以接口设计为中心。 b、在使用其他对象的...使用jmockit,不要使用jre,要用jdk,否则,可能出现比如java.lang.NoClassDefFoundError: org.junit.runner.Runner

    test.jmockit-0.998.jar包

    单元测试 mock jmockit包 JMockit 是一个轻量级的mock框架是用以帮助开发人员编写测试程序的一组工具和API

    JMockit学习分享

    对JMockit 测试工具精华的提炼和分析,关注到mock单元测试最核心的概念和思路。

    jmockit测试例子

    Mockup always comes to us when we try to ... Now, jmockit comes to us. "Tests can easily be written that will mock final classes, static methods, constructors, and so on. There are no limitations.

    jmockit1:用于集成测试,模拟,伪造和代码覆盖的高级Java库

    JMockit 1.x发行版的代码库-- 如何建立专案: ...samples / pom.xml:使用JUnit 4、5或TestNG 6的各种样本测试套件(教程,LoginService,java8testing) samples / petclinic / pom.xml:使用Java EE 8的集成测

    jmockit使用例子

    jmockit使用例子,包括两种调用方式,可以改变静态方法

    JMockit基础教程

    JMockit基础教程 录制期望(创建依赖Mock、方法调用、参数返回值、次数等) 执行测试(创建测试对象、执行待测试方法) 验证结果(结果验证如返回值;交互验证如方法调用次数,顺序)

    Springboot 3中,使用jacoco插件生成单元测试覆盖率报告

    springboot3 + jdk17 + maven + jmockit

    jmockit总结

    jmockit资料,关于jmockit的具体使用方式,以及不同方法的详解。

    test.jmockit-0.998-sources.jar

    单元测试 mock jmockit源码 JMockit 是一个轻量级的mock框架是用以帮助开发人员编写测试程序的一组工具和API

    jmockit中文教程

    jmockit中文教程

Global site tag (gtag.js) - Google Analytics