JMockit 使用
1.使用介绍
传统mock方法的限制:
JDK Proxy必须实现接口
Cglib Proxy的class和方法不能是final限定的
对于静态方法无能为力
对非public方法无能为力,或者需要花比较大的代价进行反射处理
比较依赖于Ioc机制,对于new或工厂类管理的bean无法进行有效的测试
JMockit优点
JMockit项目基于 Java 5 SE 的 java.lang.instrument 机制,内部使用 ASM 库来修改Java的Bytecode,是一个能帮我们解决以上问题的轻量级框架,它允许你动态的改变已有的方法,这主要基于java 1.5的Instrumentation框架,允许你重定义private,static and final方法,甚至是no-arg constructors都能够并轻易的重定义,这样便可以使得JMockit能够适应几乎所有的设计。
使用mock的场景
真实对象有着不确定的行为
真实对象很难创建
真实对象的行为很难触发
真实对象响应缓慢
真实对象是用户界面
真实对象使用了回调机制
真实对象尚未存在
而对应的mock具有下面的功能
替换远程对象,如ESB、WEB Service对象等
替换复杂的对象
方便模块化开发
2.JMockit原理
JMockit是依赖JDK提供的instrument机制及ASM来实现其功能的,基本原理是这样的:
在JDK装入类的时候,由于我们设置也-javaagent,JDK会查看这个jar包的/META-INF/MANIFEST.MF文件,找到Premain-Class并加载这个类,然后调用这个类的premain方法将Instrument实现设置进去,然后JMockit就可以在类加载的时候做transformer,在做transformer的时候会通过ASM来动态改变字节码。
如果大家想有更深入的也解请在这里下载源代码。
3.JMockit运行环境要求
对于JDK5之后版本,不用设置;对于JDK5,需要配置jvm参数-javaagent:jmockit.jar
在 windows-perferences-java-installedJRES-default vm argument 中设置
-javaagent:D:\xx\xx\jmockit.jar
4.JMockit使用实例
关键词:如何mock一个类的方法、Expectations
源类清单
public class DateUtil { private int type; public static final String getCurrentDateStr() { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); return sdf.format(DateUtil.now()); } public static final String getCurrentDateStrByFormatType(int type) { if (type == 1) { SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss"); return sdf.format(DateUtil.now()); } else { return DateUtil.getCurrentDateStr(); } } public static final Date now() { return new Date(); } public int getType() { return type; } public void setType(int type) { this.type = type; } }
单元测试类清单1
public class DateUtilTest { /** * Mock某个类方法 */ @Test public void testGetCurrentDateStr() { //DateUtil.class,要Mock的类 new Expectations(DateUtil.class) { { //要Mock的方法now,其他方法DateUtil.class DateUtil.now(); //期望方法返回的结果 result = mockDate(); } }; Assert.assertEquals("2010-07-22 15:52:55", DateUtil.getCurrentDateStr()); } /** * Mock 某个类方法根据不同参数返回不同值 */ @Test public void testGetCurrentDateStrByFormatType() { new Expectations(DateUtil.class) { { DateUtil.getCurrentDateStrByFormatType(anyInt); result = new Delegate() { public String getCurrentDateStrByFormatType(int type) { if (type == 1) { return "2010/07/22 15:52:55"; } else { return "2010-07-22 15:52:55"; } } }; } }; Assert.assertEquals("2010-07-22 15:52:55", DateUtil.getCurrentDateStrByFormatType(2)); } public static Date mockDate() { Calendar c = Calendar.getInstance(); c.set(2010, 6, 22, 15, 52, 55); return c.getTime(); } }
小结
Expectations:一个Expectations块是给定测试方法中将会涉及到的mock项中,预期将要被调用的方法或构造函数。一个Expectations可以包含多个预期的要执行方法(mock),但不必包含所有预期会被调用的方法。在Expectations中;除了可以指定预期的方法外,还可以指定方法的参数的精确值或约束行为(满足某个断言);同时Expectations中还可以指定该方法预期的返回值(如果有)或预期抛出的异常。Expectations(.class){}这种方式只会模拟区域中包含的方法,这个类的其它方法将按照正常的业务逻辑运行,上面的例子,定义了一个mock类DateUtil,同时在Expectation中定义了预期会被调用的方法now,以及now方法的返回值,这种方式还有种等价实现方式,使用@Mocked标签
@Test public void testGetCurrentDateStr(@Mocked(methods="now")DateUtil dateUtil) { //DateUtil.class,要Mock的类 new Expectations() { { //声明要Mock的方法(注:其它方法按照正常的业务逻辑运行) DateUtil.now(); //期望方法返回的结果 result = mockDate(); } }; Assert.assertEquals("2010-07-22 15:52:55", DateUtil.getCurrentDateStr()); }
case-2
关键词:service dao @Mocked
源类清单
public interface P4psettleDayService { public List<DaySyncVerifyDO> getDaySyncVerifyNotSuccessList(); public void updataStatusById(Integer id,String status); }
单元测试类清单
@TestCaseInfo(contextKey = "P4PSettleServicesLocator", defaultRollBack = false) public class P4psettleDayServiceImplTest extends BaseTestCase { private P4psettleDayService p4psettleDayService; public void setP4psettleDayService(P4psettleDayService p4psettleDayService) { this.p4psettleDayService = p4psettleDayService; } /** * 此例子仅为说明如何Mock我们现有service或dao的部分方法 */ @Test public void testUpdataStatusById() { new MockUp<P4psettleDayServiceImpl>(){ @Mock public List<DaySyncVerifyDO> getDaySyncVerifyNotSuccessList(){ List<DaySyncVerifyDO> list = new ArrayList<DaySyncVerifyDO>(); DaySyncVerifyDO mockDO = new DaySyncVerifyDO(); mockDO.setId(111111); list.add(mockDO); return list; } }; //这里将返回我们mock的数据 List<DaySyncVerifyDO> list = p4psettleDayService .getDaySyncVerifyNotSuccessList(); if (list != null && list.size() > 0) { for (DaySyncVerifyDO item : list) { //这里执行原有的方法 p4psettleDayService.updataStatusById(item.getId(), DayEnum.DAYSYNCVERIFY_STATUS_SUCCESS.getValue()); } } } }
小结
上面单元测试代码没有实际意义,只是说明如果我们希望mock spring注入的service或dao的一个或多个方法,就可采用上述方式
case-3
关键词:mock private方法 invoke
源类清单
还是第一个例子,Now方法是私有的
public class DateUtil { ...... private static final Date now() { return new Date(); } ...... }
单元测试类清单
public class DateUtilTest { /** * Mock某个类私有方法 */ @Test public void testGetCurrentDateStr() { //DateUtil.class,要Mock的类 new Expectations(DateUtil.class) { { //执行DateUtil的now方法 invoke(DateUtil.class,"now"); //期望方法返回的结果 result = mockDate(); } }; Assert.assertEquals("2010-07-22 15:52:55", DateUtil.getCurrentDateStr()); }
小结
mock 某个类的私有方法,用invoke(mock的类或实例,方法名,方法的参数列表)
case-4
关键词:Verifications 想验证被Mock的类的某个方法是否被调用
单元测试类清单
/** * 演示验证被Mock的类的某个方法是否被调用 * * @author LeiSir */ public class ServiceTest { @Mocked Remote remote; @Test public void testDoFuncYes() { Service service = new Service(); service.doFunc(true, 1); new Verifications() { { remote.doSomething(anyInt);//表示这个方法会被执行 //remote.doSomething(1);//表示这个方法会被执行,而且参数是1;在当前case,会通过 //remote.doSomething(2);//表示这个方法会被执行,而且参数是2;在当前case,这个会不被通过 } }; } @Test public void testDoFuncNo() { Service service = new Service(); service.doFunc(false, 1); new Verifications() { { remote.doSomething(anyInt); times = 0;//调用次数,0表示上面方法不会被调用 } }; } private static class Remote { public void doSomething(int a) { } } private static class Service { private Remote remote = new Remote(); public void doFunc(boolean flag, int a) { if (flag) { remote.doSomething(a); } } } }
小结
有时候我们想验证某个类的方法是否被正确调用的时候,上述Verifications就派上用场了
0 条评论。