zoukankan      html  css  js  c++  java
  • [转]用Mock Object进行独立单元测试

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

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

  • 使用Mock Object的场合
    1. 实际对象的行为还不确定。
    2. 实际的对象创建和初始化非常复杂。
    3. 实际对象中存在很难执行到的行为(如网络异常等)。
    4. 实际的对象运行起来非常的慢。
    5. 实际对象是用户界面程序。
    6. 实际对象还没有编写,只有接口等。
  • 简单分析用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对象。

    1. 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对象了。

    2. 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更容易理解一点。

    转自:http://blog.csdn.net/sunfmin/archive/2004/11/23/192365.aspx
查看全文
  • 相关阅读:
    How to build Linux system from kernel to UI layer
    Writing USB driver for Android
    Xposed Framework for Android 8.x Oreo is released (in beta)
    Linux Smartphone Operating Systems You Can Install Today
    Librem 5 Leads New Wave of Open Source Mobile Linux Contenders
    GUADEC: porting GNOME to Android
    Librem 5 – A Security and Privacy Focused Phone
    GNOME and KDE Join Librem 5 Linux Smartphone Party
    Purism计划推出安全开源的Linux Librem 5智能手机
    国产系统之殇:你知道的这些系统都是国外的
  • 原文地址:https://www.cnblogs.com/taoxu0903/p/941724.html
  • Copyright © 2011-2022 走看看