`
yianpuodiaotu
  • 浏览: 240230 次
  • 性别: Icon_minigender_1
  • 来自: 济南
社区版块
存档分类
最新评论

用Mock Object进行独立单元测试

阅读更多

转自:https://compdoc2cn.dev.java.net/mock/html/mock.cn.html

 

声明:文章之前,先做一下声明。本博客中所有转摘文章,转摘地址都尽可能找到『原创出处』,但不幸的是很多情形找不到或者不明确是否『原创出处』,有可能是多次『转摘』后的地址。如对您的权利带来伤害,请联系我,我会及时作出处理

 

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

1. 使用Mock Object的场合

  • 实际对象的行为还不确定。
  • 实际的对象创建和初始化非常复杂。
  • 实际对象中存在很难执行到的行为(如网络异常等)。
  • 实际的对象运行起来非常的慢。
  • 实际对象是用户界面程序。
  • 实际对象还没有编写,只有接口等。

2. 简单分析用Mock Object的原因

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

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

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

3. 具体举例

现在我们写好了类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对象。而且里面的方法体都是非常简单的,就是假定它就返回某某值。

public class MockAccountManager implements AccountManager {
    private Hashtable accounts = new Hashtable();
    public void addAccount(Account account) {
        this.accounts.put(account.getAccountId(), account);
    }
    /*
     *
     */
    public Account findAccountForUser(String userId) {
        return (Account)accounts.get(userId);
    }
    /*
     *
     */
    public void updateAccount(Account account) {

    }
}

我们这里还有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方法的测试。
4. 动态Mock对象(EasyMock和jMock)

EasyMock和jMock都是Open Source,前者在sourceforge,后者在codehaus。对这些OSS的应用可以简化我们对Mock对象的时候,大部分情况下不用手动创建Mock对象。
4.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对象了。
4.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更容易理解一点。
5. 参考资料

Manning - JUnit in Action

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

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

 

分享到:
评论

相关推荐

    dotnet 单元测试 Mock 让一个对象继承多个接口.rar

    本资源包“dotnet 单元测试 Mock 让一个对象继承多个接口.rar”提供了关于如何在.NET中使用Mock进行单元测试,特别是当对象需要实现多个接口时的详细指南。 首先,Mock对象是在单元测试中用于替代真实依赖的对象,...

    EasyMock单元测试例子

    8. **最后的清理**:在测试结束时,使用`EasyMock.reset(mockObject)`清除模拟对象的状态,以便于下一次使用。 通过"EasyMock单元测试例子"中的代码,我们可以深入学习如何设置和使用这些特性。每个示例都会包含...

    SpringBoot 多模块Dao层单元测试

    单元测试是一种软件测试方法,它针对程序中的最小可测试单元,如方法或类,进行独立验证。在Java和Spring Boot环境中,我们可以使用JUnit框架来进行单元测试,同时结合Mockito等工具来模拟对象行为,以便专注于测试...

    Junit + Hamcrest + Mockito 单元测试 Jar包

    在软件开发过程中,单元测试是确保代码质量的重要环节。它允许开发者独立地验证代码的各个模块,确保它们按预期工作。本资源包含的"Junit + Hamcrest + Mockito 单元测试 Jar包"是一个用于Java应用程序单元测试的...

    利用NunitFramework进行普通类库和涉及数据库操作的类库的单元测试(源代码)

    本篇将详细介绍如何使用NUnit Framework进行普通类库和涉及数据库操作的类库的单元测试,并提供源代码实例。 一、NUnit Framework基础知识 NUnit 是一个强大的.NET测试框架,支持多种类型的测试,包括方法级别的...

    spring-mock.jar

    而Spring Mock作为Spring框架的一个重要组成部分,为测试提供了强大的模拟(mocking)能力,帮助开发者在不依赖真实环境的情况下进行单元测试。本文将详细探讨Spring Mock的核心概念、使用方法以及它在实际开发中的...

    单元测试c#篇

    这种方式不仅有助于测试代码的独立性,而且还能确保即使在真实服务无法使用的情况下也能进行有效的单元测试。通过使用Mock对象,开发者可以在控制环境中测试代码,而不必担心外部依赖的影响,从而提高测试的质量和...

    Python 单元测试

    在Python编程中,单元测试(Unit Testing)是确保代码质量的重要环节,它允许...通过合理使用`mock`,开发者能够更好地聚焦于代码的核心逻辑,确保每个单元都能独立、准确地运行,从而提升整个项目的质量和可靠性。

    Mockito+junit5搞定单元测试

    通过以上的介绍,我们了解了如何使用 Mockito 和 JUnit 5 搭配进行单元测试。Mockito 提供了强大的模拟功能,使得测试更加灵活和可控;而 JUnit 5 提供了丰富的测试注解和功能,让编写和组织测试变得更加便捷。结合...

    数据库框架ormlite和单元测试框架junit的使用

    通过JUnit,我们可以对代码的各个部分进行独立测试,确保每个方法的功能正常,提高代码质量。 在“数据库框架ORMLite和单元测试框架junit的使用”这个模块中,首先,你需要了解ORMLite的核心概念: 1. **实体类...

    Rhino Mocks 单元测试必用

    单元测试是一种软件开发的最佳实践,它允许程序员对代码的各个小部分进行独立验证,确保它们按预期工作。通过单元测试,开发者可以在早期发现潜在的问题,提高代码质量,降低维护成本,并且便于重构。 ### 2. Rhino...

    Xunit单元测试

    首先,Xunit的核心理念是基于测试方法的组织,每个测试方法都是一个独立的断言集合。相比于传统的NUnit或 MSTest,Xunit引入了理论(Theories)的概念,允许对一组输入数据进行测试,提高了测试的覆盖率和效率。此外...

    C#单元测试完全指南:构建可靠的代码基石

    #### 使用NUnit进行单元测试 ##### 安装NUnit 在Visual Studio中,可以通过NuGet包管理器轻松安装NUnit。打开“管理NuGet包”窗口,搜索"NUnit"并安装最新版本。 ##### 创建测试项目 在Visual Studio中创建一个新的...

    GMock——groovy下的mock工具

    ### 使用 GMock 进行单元测试 1. **安装和引入**:在给定的压缩包中,`gmock-0.8.0.jar` 是 GMock 的库文件,将其添加到项目的类路径中,即可开始使用。 2. **创建 Mock 类**:首先,我们需要为要模拟的类创建一个...

    15、Mock替身1

    `createMock()`方法接收一个类名作为参数,返回一个`MockObject`,这个对象的行为可以根据测试需求进行定制。 例如,在测试`PostFactory`时,如果`PostFactory`依赖于`Post`类,我们可以通过`createMock(Post::...

    基于MOQ 单元测试的 demo程序

    在.NET开发环境中,特别是使用C#语言时,MOQ是一个非常流行的模拟框架,用于简化接口和抽象类的模拟,从而实现对依赖项的隔离,以便进行单元测试。本篇将深入讲解基于MOQ的单元测试,帮助你理解其基本概念、用法以及...

Global site tag (gtag.js) - Google Analytics