2009-01-15 61 views
3

我的第一个编程工作向我介绍了单元测试和模拟对象的概念,但总觉得有些问题。如何编写模拟对象?

比方说,我们正在编写一个银行应用程序,并嘲笑一个的BankAccount对象所需:


    // boilerplate code 
    public interface IBankAccount { 
     void Deposit(int amount); 
     void Withdrawal(int amount); 
     int getBalance(); 
     int getAccountNumber(); 
    } 

    public interface IBankAccountFactory { 
     IBankAccount getAccount(int accountNumber); 
    } 

    public class ProductionBankAccountFactory implements IBankAccountFactory { 
     public IBankAccount getAccount(int accountNumber) { 
      return new RealBankAccount(accountNumber); 
     } 
    } 

    public class MockBankAccountFactory implements IBankAccountFactory { 
     public IBankAccount getAccount(int accountNumber) { 
      return new MockBankAccount(accountNumber); 
     } 
    } 

    public static class BankAccountFactory { 
     // ewww, singletons! 
     public static IBankAccountFactory Instance; 
    } 

    // finally, my actual business objects 
    public class MockBankAccount implements IBankAccount { 
     public MockBankAccount(int accountNumber) { ... } 
     // interface implementation 
    } 

    public class RealBankAccount implements IBankAccount { 
     public RealBankAccount(int accountNumber) { ... } 
     // interface implementation 
    } 

每个类都有一个目的:

  • 工厂和工厂接口存在的收官构造函数给我们的模拟和实物。
  • 静态BankAccountFactory类允许我们在生产应用程序或测试的开始分别分配BankAccountFactory.Instance IRealBankAccountFactory或MockBankAccountFactory的实例。
  • 一旦一切都设置正确,任何类可以简单地通过调用抢IBankAccount的一个实例:

    BankAccountFactory.Instance.getAccount(accountNum); 

这工作,但它在导致的样板代码很多。我不应该为每个我想模拟的课程写5个新课程。我相信有一个更简单的方法,所以我必须问SO社区:

有没有更好的或首选的方式来编写模拟对象?

[编辑补充:]我很欣赏的联系,以嘲讽和DI框架,但现在我正在一个500 KLOC应用,以及代码的至少60%由样板模拟的上述风格的类。

我只是想减少代码库的大小,而不需要为Yet-Another-Framework™重新编写大块代码,所以它可以帮助我更多地看到手工编写的模拟类。 :)

回答

0

有迹象表明,通过允许您指定对象简化流程模拟库和它的单元测试代码的行为。

一个很好的例子是起订量库(http://code.google.com/p/moq/

1

我想我的第一个问题就是为什么你需要使用工厂模式来包装你的对象的建设;特别是你的Mock对象。由于套件中的每个单元测试应该完全独立于任何其他单元测试运行,因此您似乎可以直接在单元测试类的setUp方法中甚至在测试本身中实例化您的MockBankAccount。如果您使用的工厂,以单元测试

public interface IBankAccount { 
    void Deposit(int amount); 
    void Withdrawal(int amount); 
    int getBalance(); 
    int getAccountNumber(); 
} 

public class MockBankAccountFactory implements IBankAccountFactory { 
    public IBankAccount getAccount(int accountNumber) { 
     return new MockBankAccount(accountNumber); 
    } 
} 

public class BankAccountUnitTest extends TestCase { 
    IBankAccount testBankAccount; 

    public void setUp() { 
     testBankAccount = new MockBankAccount(someAccountNumber); 
    } 

    // Unit tests here 
} 

另一类使用 IBankObject,那么你应该看看dependency injection提供:如果我在上面的情况,我会写这样的事情一个模拟对象,而不是被测试的类实例化一个模拟对象。