jmockit

首页 文章 文库 视频 iprocess 课程 认证 招聘 咨询 工具 图书 讲座吧  
会员   
 
  
每天15篇文章      
不但获得谋生技能  
更可以追随信仰    
 
 
  
  
分享到
使用JMockit编写java单元测试
 
作者 chjttony,火龙果软件    发布于 2014-1-13
 

之前《有效使用Mock编写java单元测试》一文中层介绍过使用EasyMock和PowerMock来编写java单元测试,今天介绍一个更加强大的工具——JMockit。

引用单元测试中mock的使用及mock神器jmockit实践中的java单元测试中各种Mock框架对比,就能明白JMockit有多么强大:

JMockit是基于JavaSE5中的java.lang.instrument包开发,内部使用ASM库来动态修改java的字节码,使得java这种静态语言可以想动态脚本语言一样动态设置被Mock对象私有属性,模拟静态、私有方法行为等等,对于手机开发,嵌入式开发等要求代码尽量简洁的情况下,或者对于被测试代码不想做任何修改的前提下,使用JMockit可以轻松搞定很多测试场景。

通过如下方式在maven中添加JMockit的相关依赖:

<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测试的方法如下:

public class MyObject {
    public String hello(String name){
        return "Hello " + name;
    }
}

使用JMockit编写的单元测试如下:

@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块。

上述的例子使用了非局部模拟,下面我们使用局部模拟来改写上面的测试,代码如下:

@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;
        }
    };
}

模拟静态方法:

@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:

@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对象私有属性值的方法,代码如下:

被测试代码:

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设置私有属性:

@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设置静态私有属性:

@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。

被测试的代码如下:

public class StateMocked {
    
    public static int getDouble(int i){
        return i*2;
    }
    
    public int getTriple(int i){
        return i*3;
    }
}

改写普通方法内容:

@Test
public void testMockNormalMethodContent() throws IOException {
    final StateMocked obj = new StateMocked();
    new NonStrictExpectations(obj) {
        {
            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类中
}

修改静态方法的内容:

若要改写静态方法内容,则首先需要新建一个类,包含与被测试静态方法相同方法签名的方法,并且使用@Mock标注,代码如下:

public class StaticMocked {
    @Mock
    public static int getDouble(int i){
        return i*20;
    }
}

测试代码如下:

@Test
public void testDynamicMockStaticMethodContent() throws IOException {
    Mockit.setUpMock(StateMocked.class, StaticMocked.class);
    assertEquals(20, StateMocked.getDouble(1));
    assertEquals(40, StateMocked.getDouble(2));
    Mockit.tearDownMocks();
}

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文件中添加如下的插件:

<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的单元测试覆盖率。

   
相关文章相关文档 相关课程



性能测试十问:测试经理篇
web前端性能优化进阶路
性能测试综述
VS2010中自动化测试—Web性能测试
性能测试
性能测试执行之测试脚本录制
性能测试进阶1-流程篇
性能测试进阶2-方案篇
性能测试方法与技术
使用LoadRunner进行性能测试
Android应用的性能测试
基于Android的单元、性能测试
分享到
 
 
 
   


LoadRunner性能测试基础
软件测试结果分析和质量报告
面向对象软件测试技术研究
设计测试用例的四条原则
功能测试中故障模型的建立
性能测试综述
更多...   


性能测试方法与技术
测试过程与团队管理
LoadRunner进行性能测试
WEB应用的软件测试
手机软件测试
白盒测试方法与技术

相关咨询服务
建立软件测试规范
性能评测与调优


某知名博彩公司 性能测试
ABB 单元测试方法与实践
北京软件测试方法与实践
北京 测试需求分析与测试用例
北京 白盒测试方法与技术
单元测试、重构及持续集成
CNNIC 自动化测试
某研发中心 单元测试重构
神华信息 .NET单元测试
更多...   
 
 
 
 
 
 
 
希望我们的资料可以帮助你学习,也欢迎投稿&提建议给我
频道编辑:winner
邮       件:winner@uml.net.cn

关于我们 | 联系我们 |   京ICP备10020922号  京公海网安备110108001071号

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值