Mock Objectによる独立ユニットテスト

7185 ワード

Mock Objectによる独立ユニットテスト(Testing in isolation with mock objects)
独立テストとは、呼び出した他のクラスやメソッドのコードをテストするのではなく、1つのクラスやメソッドのコードを単独でテストすることです.すなわち、呼び出された他のクラスまたはメソッドが正常に実行されていると仮定します.
  • 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の2つのメソッドです.ただし、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は、いずれもオープンソースであり、前者はsourceforgeであり、後者はcodehausである.これらのOSSの適用は、Mockオブジェクトを手動で作成することなく、Mockオブジェクトを簡単に作成することができます.
  • EasyMock AccountServiceのtransferメソッドをテストし、AccountService 2 Testsクラスを書きました.
    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メソッドをテストするAccountService 3 Tests単体テスト
    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