java partial class_easymock教程-partial class mocking

easymock中提供对于类的mock功能,我们可以方便的mock这个类的某些方法,指定预期的行为以便测试这个类的调用者。这种场景下被mock的类在测试案例中扮演的是次要测试对象或者说依赖的角色,主要测试对象是这个mock类的调用者。但是有时候我们需要将这个测试类作为主要测试对象,我们希望这个类中的部分(通常是大部分)方法保持原有的正常行为,只有个别方法被我们mock掉以便测试。

1. 使用方法

我们先来看看这个partial class mocking 是如何工作的:

1fa987a29c6482f53d401256f96355eb.png

ca75c07623e1b494fee67e8f316fc310.gifpublicclassService{

d18c02628675d0a2c816449d98bda930.png

97e794c86028c5f5b5461ae5ef440a4c.png

3c6cafce68eb941a00f1998f1d3d3aa6.gifpublicvoidexecute(){

d18c02628675d0a2c816449d98bda930.png            actualMethod();

d18c02628675d0a2c816449d98bda930.png            needMockMethod();

ecedf933ec37d714bd4c2545da43add2.png        }d18c02628675d0a2c816449d98bda930.png

97e794c86028c5f5b5461ae5ef440a4c.png

3c6cafce68eb941a00f1998f1d3d3aa6.gifvoidactualMethod(){

d18c02628675d0a2c816449d98bda930.png            System.out.println("call actualMethod()");

ecedf933ec37d714bd4c2545da43add2.png        }d18c02628675d0a2c816449d98bda930.png

97e794c86028c5f5b5461ae5ef440a4c.png

3c6cafce68eb941a00f1998f1d3d3aa6.gifpublicvoidneedMockMethod(){

d18c02628675d0a2c816449d98bda930.png            System.out.println("call needMockMethod()");

ecedf933ec37d714bd4c2545da43add2.png        }d18c02628675d0a2c816449d98bda930.png

8f1ba5b45633e9678d1db480c16cae3f.png    }

我们给出了一个非常简单的类,我们将要测试execute()方法,期望能测试到actualMethod()这个方法的正常行为,然后需要mock掉needMockMethod().

1fa987a29c6482f53d401256f96355eb.png

ca75c07623e1b494fee67e8f316fc310.gifpublicclassPartialClassMockTestextendsAssert{

d18c02628675d0a2c816449d98bda930.png

d18c02628675d0a2c816449d98bda930.png    @Test

97e794c86028c5f5b5461ae5ef440a4c.png

3c6cafce68eb941a00f1998f1d3d3aa6.gifpublicvoidtestPartialMock(){

d18c02628675d0a2c816449d98bda930.png        Service service=EasyMock.createMockBuilder(Service.class).addMockedMethod("needMockMethod").createMock();

d18c02628675d0a2c816449d98bda930.png        service.needMockMethod();

d18c02628675d0a2c816449d98bda930.png        EasyMock.expectLastCall();

d18c02628675d0a2c816449d98bda930.png

d18c02628675d0a2c816449d98bda930.png        EasyMock.replay(service);

d18c02628675d0a2c816449d98bda930.png        service.execute();

d18c02628675d0a2c816449d98bda930.png        EasyMock.verify(service);

ecedf933ec37d714bd4c2545da43add2.png    }8f1ba5b45633e9678d1db480c16cae3f.png}

上面的测试案例运行通过,输出为"call actualMethod()",没有"call needMockMethod()",说明我们设置的mock生效了。我们创建的mock类的确是只有部分我们制定的方法是mock的,其他都是正常行为。

再来看看为什么我们要需要partial class mocking 这个功能?为什么需要mock掉其中的一个方法?

我们来看看下面这个更加真实的例子:

1fa987a29c6482f53d401256f96355eb.png

ca75c07623e1b494fee67e8f316fc310.gifpublicclassService{

d18c02628675d0a2c816449d98bda930.png

97e794c86028c5f5b5461ae5ef440a4c.png

3c6cafce68eb941a00f1998f1d3d3aa6.gifpublicString execute2(){

d18c02628675d0a2c816449d98bda930.pngreturngetConfiguration();

ecedf933ec37d714bd4c2545da43add2.png        }d18c02628675d0a2c816449d98bda930.png

97e794c86028c5f5b5461ae5ef440a4c.png

3c6cafce68eb941a00f1998f1d3d3aa6.gifpublicString getConfiguration(){

d18c02628675d0a2c816449d98bda930.pngreturnConfiguration.getUsername();

ecedf933ec37d714bd4c2545da43add2.png        }8f1ba5b45633e9678d1db480c16cae3f.png    }4f1150b881333f12a311ae9ef34da474.png

1fa987a29c6482f53d401256f96355eb.png

ca75c07623e1b494fee67e8f316fc310.gifpublicclassConfiguration{

97e794c86028c5f5b5461ae5ef440a4c.png

3c6cafce68eb941a00f1998f1d3d3aa6.gifpublicstaticString getUsername(){

d18c02628675d0a2c816449d98bda930.png//ignore the code to get configuration from file or databased18c02628675d0a2c816449d98bda930.pngreturn"username";

ecedf933ec37d714bd4c2545da43add2.png        }8f1ba5b45633e9678d1db480c16cae3f.png    }

这里例子中,需要测试的 execute2()方法需要调用getConfiguration()方法,而getConfiguration()方法则调用了Configuration的静态方法来获取配置信息。我们假设读取配置的代码比较复杂不能直接在单元测试环境下运行,因此通过情况下这里的execute2()方法就会因为这个getConfiguration()而造成无法测试。因此我们可以考虑通过partial class mocking的功能来mock掉getConfiguration()方法从而使得我们的测试案例可以覆盖到execute2()方法

4f1150b881333f12a311ae9ef34da474.png@Test

1fa987a29c6482f53d401256f96355eb.png

ca75c07623e1b494fee67e8f316fc310.gifpublicvoidtestStaticMethod(){

d18c02628675d0a2c816449d98bda930.png        Service service=EasyMock.createMockBuilder(Service.class).addMockedMethod("getConfiguration").createMock();

d18c02628675d0a2c816449d98bda930.png        EasyMock.expect(service.getConfiguration()).andReturn("abc");

d18c02628675d0a2c816449d98bda930.png

d18c02628675d0a2c816449d98bda930.png        EasyMock.replay(service);

d18c02628675d0a2c816449d98bda930.png        assertEquals("abc", service.execute2());

d18c02628675d0a2c816449d98bda930.png        EasyMock.verify(service);

8f1ba5b45633e9678d1db480c16cae3f.png    }

这个测试案例可以正常通过,我们通过partial class mocking成功的避开了getConfiguration()这个绊脚石。

当然这里的实例代码本身就有点问题,应该采用DI的方法将configuration注入进来,而不是在内部通过静态方法来获取。因此一个建议是在使用partial class mocking功能前,先看看是不是可以通过重构来显改进测试类。只有当我们有足够充分的不得已的理由时,才使用partial class mocking这种变通(或者说取巧)的方式来解决问题。

2. 限制

上面两个例子中,我们仔细看看会发现,被mock的方法都是public的。我们试着将方法修改为protected和default,partial class mocking依然生效。但是修改为private之后,则抛出异常:

java.lang.IllegalArgumentException: Method not found (or private): needMockMethod

at org.easymock.internal.MockBuilder.addMockedMethod(MockBuilder.java:75)

at net.sourcesky.study.easymock.tutorial.PartialClassMockTest.testPartialMock(PartialClassMockTest.java:52)

或者将mock的方法继续保持public,但是加上final,则抛出以下异常:

java.lang.IllegalStateException: no last call on a mock available

at org.easymock.EasyMock.getControlForLastCall(EasyMock.java:521)

at org.easymock.EasyMock.expectLastCall(EasyMock.java:512)

at net.sourcesky.study.easymock.tutorial.PartialClassMockTest.testPartialMock(PartialClassMockTest.java:54)

我们回到之前的章节,class mocking里面讲述了class mocking的一些限制:private方法和final方法是不能mock的。partial class mocking下这些限制依然存在。因此,为了开启partial class mocking,我们不得不稍微破坏一下类的封装原则,对于原本应该是private的方法,修改为protected或者default。

不得不再次申明,partial class mocking不是一个足够好的解决方案,它只适合在不得已的情况下使用,不要太依赖这个特性。重构代码改善代码才是王道。3. 疑问

另外class mocking中还讲到,对于类的equals(), toString()和hashCode()这三个方法,class mocking下是easymock为这三个方法内建了easymock的实现,因此也不能mock。而partial class mocking,这三个方法同样不能mock,但是easymock不再为它们内建实现,而是使用它们正常的功能。

关于这点还是有一点疑问,我在easymock的官方文档中看到以下描述

Remark: EasyMock provides a default behavior for Object's methods (equals, hashCode, toString). However, for a partial mock, if these methods are not mocked explicitly, they will have their normal behavior instead of EasyMock default's one.

言下之意,似乎equals, hashCode, toString这三个方法还是可以显式mock的。但是我测试了一下:

1fa987a29c6482f53d401256f96355eb.png

ca75c07623e1b494fee67e8f316fc310.gifpublicclassService{

d18c02628675d0a2c816449d98bda930.png

97e794c86028c5f5b5461ae5ef440a4c.png

3c6cafce68eb941a00f1998f1d3d3aa6.gifpublicString execute3(){

d18c02628675d0a2c816449d98bda930.png            actualMethod();

d18c02628675d0a2c816449d98bda930.pngreturntoString();

ecedf933ec37d714bd4c2545da43add2.png        }d18c02628675d0a2c816449d98bda930.png

d18c02628675d0a2c816449d98bda930.png        @Override

97e794c86028c5f5b5461ae5ef440a4c.png

3c6cafce68eb941a00f1998f1d3d3aa6.gifpublicString toString(){

d18c02628675d0a2c816449d98bda930.pngreturn"defaultToString()";

ecedf933ec37d714bd4c2545da43add2.png        }8f1ba5b45633e9678d1db480c16cae3f.png    }4f1150b881333f12a311ae9ef34da474.png

4f1150b881333f12a311ae9ef34da474.png    @Test

1fa987a29c6482f53d401256f96355eb.png

ca75c07623e1b494fee67e8f316fc310.gifpublicvoidtestToStringMethod(){

d18c02628675d0a2c816449d98bda930.png        Service service=EasyMock.createMockBuilder(Service.class).addMockedMethod("toString").createMock();

d18c02628675d0a2c816449d98bda930.png        EasyMock.expect(service.toString()).andReturn("abc");

d18c02628675d0a2c816449d98bda930.png        EasyMock.replay(service);

d18c02628675d0a2c816449d98bda930.png        assertEquals("abc", service.execute3());

d18c02628675d0a2c816449d98bda930.png        EasyMock.verify(service);

8f1ba5b45633e9678d1db480c16cae3f.png    }

toString()方法的mock没能生效,抛出异常:

java.lang.IllegalStateException: no last call on a mock available

at org.easymock.EasyMock.getControlForLastCall(EasyMock.java:521)

at org.easymock.EasyMock.expect(EasyMock.java:499)

at net.sourcesky.study.easymock.tutorial.PartialClassMockTest.testToStringMethod(PartialClassMockTest.java:74)

可以看到明显是EasyMock.expect(service.toString()).andReturn("abc"); 这里的record没有成功。

posted on 2010-11-30 14:23 sky ao 阅读(2859) 评论(0)  编辑  收藏 所属分类: software test

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值