分享
 
 
 

[原创]用Mock Object进行独立单元测试(Testing in isolation with mock objects)

王朝java/jsp·作者佚名  2006-01-09
窄屏简体版  字體: |||超大  

用Mock Object进行独立单元测试(Testing in isolation with mock objects)

独立测试就是单独测试一个类或方法里的代码,而不测试里面调用的其他类或方法的代码。即假定调用的其他类或方法都正常执行。

使用Mock Object的场合

实际对象的行为还不确定。

实际的对象创建和初始化非常复杂。

实际对象中存在很难执行到的行为(如网络异常等)。

实际的对象运行起来非常的慢。

实际对象是用户界面程序。

实际对象还没有编写,只有接口等。

简单分析用Mock Object的原因

假定我们有对象A,它内部频繁调用了B中的方法。但是除了调用B的部分还存在自己的逻辑代码。这时,我们只想写A的单体测试,而不想测试B(因为B可能还没编写,只写了实现的接口或者B运行太慢了影响我们的测试效率)。这样的话,我们就需要创建B的Mock对象。

换言之,我们现在不关心B,我们假定B的行为都能正常运行。我们的目标是假定B运行正常的情况下,来对A进行单体测试。

Mock对象就是我们先不用关心的对象,但是我们的关注对象对它有调用,所以我们给关注对象准备个假货让它用。

具体举例

现在我们写好了类AccountService,具体如下

public class AccountService {

private AccountManager accountManager;

public void setAccountManager(AccountManager manager) {

this.accountManager = manager;

}

public void transfer(String senderId, String beneficiaryId, long amount) {

Account sender = this.accountManager.findAccountForUser(senderId);

Account beneficiary =

this.accountManager.findAccountForUser(beneficiaryId);

sender.debit(amount);

beneficiary.credit(amount);

this.accountManager.updateAccount(sender);

this.accountManager.updateAccount(beneficiary);

}

}

现在我们想测试transfer方法,它内部调用的AccountManager的两个方法。但是对于AccountManager来说,它只是个接口,如下:

public interface AccountManager {

Account findAccountForUser(String userId);

void updateAccount(Account account);

}

所以现在我们必须些个MockAccountManager对象。而且里面的方法体都是非常简单的,就是假定它就返回某某值。

我们这里还有Account类

public class Account {

private String accountId;

private long balance;

public Account(String accountId, long initialBalance) {

this.accountId = accountId;

this.balance = initialBalance;

}

public void debit(long amount) {

this.balance -= amount;

}

public void credit(long amount) {

this.balance += amount;

}

public long getBalance() {

return this.balance;

}

public String getAccountId() {

return accountId;

}

}

由于这里的Account类非常的简单,以至于我们在测试AccountService的时候,创不创建Account的Mock对象都一样,那么我们为什么不用真实的对象测试呢。这时我们可以回想一下我们使用Mock对象的那些种场合。

下面是对AccountService的transfer方法的测试

public class AccountService1Tests extends TestCase {

public void testTransfer(){

AccountService as = new AccountService();

MockAccountManager mockAccountManager = new MockAccountManager();

Account accountA = new Account("A",3000);

Account accountB = new Account("B",2000);

mockAccountManager.addAccount(accountA);

mockAccountManager.addAccount(accountB);

as.setAccountManager(mockAccountManager);

as.transfer("A","B",1005);

assertEquals(accountA.getBalance(),1995);

assertEquals(accountB.getBalance(),3005);

}

}

这里我们在假定AccountManager方法都工作正常的情况下,完成了对transfer方法的测试。

动态Mock对象(EasyMock和jMock)

EasyMock和jMock都是Open Source,前者在sourceforge,后者在codehaus。对这些OSS的应用可以简化我们对Mock对象的时候,大部分情况下不用手动创建Mock对象。

EasyMock

现在我们还是要测试AccountService的transfer方法,我们写了AccountService2Tests类。

public class AccountService2Tests extends TestCase{

public void testTransfer(){

// setup

//创建MockControl对象,关联到AccountManager

MockControl control = MockControl.createControl(AccountManager.class);

//得到一个运行中的mock对象

AccountManager mockAccountManager =(AccountManager) control.getMock();

AccountService as = new AccountService();

as.setAccountManager(mockAccountManager);

//expect

/* 设定Mock对象的行为 */

Account accountA = new Account("A",3000);

Account accountB = new Account("B",2000);

//设定在运行中执行方法findAccountForUser("A"),并且让它返回accountA对象。

mockAccountManager.findAccountForUser("A");

control.setReturnValue(accountA);

//设定在运行中执行方法findAccountForUser("B"),并且让它返回accountB对象。

mockAccountManager.findAccountForUser("B");

control.setReturnValue(accountB);

//设定在运行中执行方法updateAccount(accountA)。

mockAccountManager.updateAccount(accountA);

mockAccountManager.updateAccount(accountB);

/* 带Mock对象运行测试 */

control.replay();

as.transfer("A","B",1005);

assertEquals(accountA.getBalance() , 1995);

assertEquals(accountB.getBalance() , 3005);

control.verify();

}

}

这里我们没有使用MockAccountManager对象,而是用EasyMock创建的动态Mock对象。所以我们就不用手动编写Mock对象了。

jMock

下面是用jMock写的测试AccountService的transfer方法的AccountService3Tests单体测试

public class AccountService3Tests extends MockObjectTestCase {

public void testTransfer(){

Mock mock = new Mock(AccountManager.class);

AccountManager mockAccountManager =(AccountManager)mock.proxy();

AccountService as = new AccountService();

as.setAccountManager(mockAccountManager);

Account accountA = new Account("A",3000);

Account accountB = new Account("B",2000);

// expectations

mock.expects(once()).method("findAccountForUser").with(same("A")).will(returnValue(accountA));

mock.expects(once()).method("findAccountForUser").with(same("B")).will(returnValue(accountB));

mock.expects(once()).method("updateAccount").with(same(accountA)).isVoid();

mock.expects(once()).method("updateAccount").with(same(accountB)).isVoid();

//excute

as.transfer("A","B",1005);

assertEquals(accountA.getBalance() , 1995);

assertEquals(accountB.getBalance() , 3005);

verify();

}

}

从上面的测试可以看到,在使用jMock做测试的时候,必须继承的是MockObjectTestCase。因为要使用once,same,returnValue等方法。其他的和EasyMock基本上相似,只是jMock更容易理解一点。

参考资料

Manning - JUnit in Action

http://www.easymock.org/Documentation.html

http://www.jmock.org/getting-started.html

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有