`
huinet
  • 浏览: 83581 次
  • 性别: Icon_minigender_1
  • 来自: 郑州市
社区版块
存档分类
最新评论

jmock 单元测试

    博客分类:
  • J2EE
阅读更多

JMock是帮助创建mock对象的工具,它基于Java开发,在Java测试与开发环境中有不可比拟的优势,更重要的是,它大大简化了虚拟对象的使用。本文中,通过一个简单的测试用例来说明JMock如何帮助我们实现这种孤立测试。

    我们在测试某类时,由于它要与其他类发生联系,因此往往在测试此类的代码中也将与之联系的类也一起测试了。这种测试,将使被测试的类直接依赖于其他类,一旦其他类发生改变,被测试类也随之被迫改变。更重要的是,这些其他类可能尚未经过测试,因此必须先测试这些类,才能测试被测试类。这种情况下,测试驱动开发成为空谈。而如果其他类中也引用了被测试类,我们到底先测试哪一个类?因此,在测试中,如果我们能将被测试类孤立起来,使其完全不依赖于其他类的具体实现,这样,我们就能做到测试先行,先测试哪个类,就先实现哪个类,而不管与之联系的类是否已经实现。

虚拟对象(mock object)就是为此需要而诞生的。它通过JDK中的反射机制,在运行时动态地创建虚拟对象。在测试代码中,我们可以验证这些虚拟对象是否被正确地调用了,也可以在明确的情况下,让其返回特定的假想值。而一旦有了这些虚拟对象提供的服务,被测试类就可以将虚拟对象作为其他与之联系的真实对象的替身,从而轻松地搭建起一个很完美的测试环境。

JMock是帮助创建mock对象的工具,它基于Java开发,在Java测试与开发环境中有不可比拟的优势,更重要的是,它大大简化了虚拟对象的使用。

本文中,通过一个简单的测试用例来说明JMock如何帮助我们实现这种孤立测试。有三个主要的类,User,UserDAO,及UserService。本文中,我们只需测试UserService,准备虚拟UserDAO。对于User,由于本身仅是一个过于简单的POJO,可以不用测试。但如果你是一个完美主义者,也可以使用JMock的虚拟它。在这领域,JMock几乎无所不能。:)

User是一个POJO,用以在视图中传输数据及映射数据库。其代码如下:

package com.sarkuya.model;

public class User {
    private String name;

    public User() {
    }
    
    public User(String name) {
        this.name = name;
    }
    
    public String getName() {
        return name;
    }
    
    public void setName(String name) {
        this.name = name;
    }
}




UserDAO负责与数据库打交道,通过数据库保存、获取User的信息。尽管我们可以不用知道JMock如何通过JDK的反射机制来实现孤立测试,但至少应知道,JDK的反射机制要求这些在运行时创建的动态类必须定义接口。在使用JMock的环境中,由于我们要虚拟UserDAO,意味着UserDAO必须定义接口。代码如下:

package com.sarkuya.dao;

import com.sarkuya.model.User;

public interface UserDAO {
    public void saveUser(User user);
    public User getUser(Long id);
}



UserService存有UserDAO的引用,通过其对外提供应用级的服务。相应地,我们先定义了其接口(尽管在本文中,作为被测试类,UserService不需要有接口,但如果以后此类需要被虚拟,也应该带有接口,基于此原因,我们也为其定义了接口)。

package com.sarkuya.service;

import com.sarkuya.dao.UserDAO;
import com.sarkuya.model.User;

public interface UserService {
    public void setUserDAO(UserDAO userDAO);
    
    public void saveUser(User user);
    public User getUser(Long id);
}



可以看到,除了setUserDAO()外,其另外的方法与UserDAO一样。这是设计模式中门面模式的典型应用,应用只通过UserService提供服务,而UserService在内部通过调用UserDAO来实现相应的功能。

根据测试先行的原则,你应该先写测试,再编写实现。这里先编写实现的原因,主要是使读者更加清楚我们接着要测试什么。由于本文是着重介绍JMock的使用,加上UserServiceImpl比较简单,因此先列出其代码如下:

package com.sarkuya.service.impl;

import com.sarkuya.dao.UserDAO;
import com.sarkuya.model.User;
import com.sarkuya.service.UserService;

public class UserServiceImpl implements UserService {
    private UserDAO userDAO;
    
    public UserServiceImpl() {
    }

    public void setUserDAO(UserDAO userDAO) {
        this.userDAO = userDAO;
    }

    public User getUser(Long id) {
        return userDAO.getUser(id);
    }

    public void saveUser(User user) {
        userDAO.saveUser(user);
    }
}



下面是UserService的测试代码:

package com.sarkuya.service;

import com.sarkuya.dao.UserDAO;
import com.sarkuya.model.User;
import com.sarkuya.service.impl.UserServiceImpl;
import junit.Framework.*;
import org.jmock.Mock;
import org.jmock.MockObjectTestCase;

public class UserServiceTest extends MockObjectTestCase {
    private UserService userService = new UserServiceImpl();
    
    private Mock userDAO = null;
    
    public UserServiceTest(String testName) {
        super(testName);
    }
    
    protected void setUp() throws Exception {
        userDAO = new Mock(UserDAO.class);
        userService.setUserDAO((UserDAO)userDAO.proxy());
    }
    
    protected void tearDown() throws Exception {
    }
    
    public static Test suite() {
        TestSuite suite = new TestSuite(UserServiceTest.class);
        
        return suite;
    }
    
    public void testGetUser() {
        User fakeUser = new User("John");
        userDAO.expects(once()).method("getUser").with(eq(1L)).will(returnValue(fakeUser));
        
        User user = userService.getUser(1L);
        assertNotNull(user);
        assertEquals("John", user.getName());
    }
    
    public void testSaveUser() {
        User fakeUser = new User("John");
        
        userDAO.expects(once()).method("getUser").with(eq(1L)).will(returnValue(fakeUser));
        User user = userService.getUser(1L);
        assertEquals("John", user.getName());
        
        userDAO.expects(once()).method("saveUser").with(same(fakeUser));
        user.setName("Mike");
        userService.saveUser(user);
        
        userDAO.expects(once()).method("getUser").with(eq(1L)).will(returnValue(user));
        User modifiedUser = userService.getUser(1L);
        assertEquals("Mike", user.getName());
    }
}



此段代码有几点应注意:

1、此测试类继承了JMock的MockObjectTestCase

2、private Mock userDAO = null;说明userDao是一个准备虚拟的对象

3、在setup()中,将userDAO.class传入Mock()后,再通过proxy()方法返回一个UserDAO的代理类实例(即虚拟对象实例),并赋值于userService

4、在testGetUser()方法中,如果我们先将第一行及第二行代码屏蔽掉,可以看出,这是一个真实环境下的测试代码。先获取一个User,然后确认其非空值,再确认其姓名为“John”。此时,在真实环境下,这段代码要测试成功的前提必须是UserDAO已经连接到了数据库,然后返回一个User后传给UserService。
但问题是,到目前为止,且不说UserDAO还未经历连接数据库这一系列繁琐而痛苦的过程,我们甚至还未实现UserDAO的接口!那么,为何加上第一行及第二行代码后就可以了呢?这正是JMock的威力所在。先实例化一个测试用的fakeUser,然后通过一系列的指令,在第二行代码中告诉JMock应该如何“做假”。尽管这句代码很长,我们可作如下理解:
1) userDAO.expects(once()):我们期望userDAO的某方法被执行一次,如果此方法未被执行,或者执行了二次以上,测试就不会通过
2) method("getUser"):这个期望被执行一次的方法名为userDAO.getUser()
3) with(eq(1L)):执行getUser()方法时,确认其传入的参数值为“1L”
4) will(returnValue(fakeUser)):上述条件均满足后,返回一个虚假的对象,即我们前面实例化的fakeUser
总体来说,当设定好第二行语句后,JMock就在后台监控着,确保userDAO.getUser()必须,且只被执行一次,且参数“1L”已经正确地传给了此方法,一旦这些条件被满足,就返回fakeUser。
而在第三行,User user = userService.getUser(1L)将触发所有这些条件,作为奖励,它接受了奖品fakeUser并赋值于user对象。而下面第四行及第五行均对此user对象进行测试,不通过才怪。

5) testSaveUser()方法中的原理类似。其思路是,将id为“1”的user从数据库中取出,将其名改为“Mike”,再存回数据库,然后再从数据库中取出此user,确保其名字已被改变。
第五行userDAO.expects(once()).method("saveUser").with(same(fakeUser))比较特殊。首先,with(same(fakeUser))说明,传入参数必须是fakeUser此实例,尽管我们在下面的语句中通过user.setName("Mike"),但只是改变了其name的属性,而fakeUser的实例引用并未发生改变,因此可以满足条件。其次,其后没有.will(returnValue(fakeUser)),因为userDAO.saveUser()不需要返回任何对象或基本数据类型。
另外,当再次执行userDAO.expects()时,JMock将重设其监控条件。我们也可以通过userDAO.reset()来显式是清除监控条件。

通过以上实例代码及其说明,我们看出,用好JMock的关键是先设置监控条件,再写相应的测试语句。一旦设好监控条件后,在某段代码块执行完毕时,如果监控条件未得到满足,或是没有通过expects()再次重设条件,或通过reset()来显式是清除监控条件,测试将无法通过。

以上介绍了JMock的基本使用方法。而这种基本用法,占了全面掌握JMock所需学习的知识70%以上。关于JMock的更多细节,感兴趣的读者可以访问JMock的网站进一步学习。

分享到:
评论

相关推荐

    JMock单元测试

    通过上述知识点,我们能够掌握JMock单元测试的基本原理和实践方法。这对于提高Java程序的测试覆盖率、增强代码质量以及快速定位和解决问题具有重大意义。随着对JMock使用的深入,可以逐渐体会到Mock技术在编写高质量...

    JMOCK使用文档

    - `JMock单元测试.doc`:可能包含具体的JMock使用示例和详细解释。 - `Jmock学习文档.docx`:更深入的JMock教程,可能包括高级用法和最佳实践。 - `Junit使用.ppt`:可能是一个关于JUnit基本用法和如何与JMock...

    jmock-1.0.1-src.zip_单元测试

    不管是测试驱动开发或者是其它的开发模式,都会需要对代码进行单元测试,对于关联依赖关系少或者比较简单的类来说,直接使用JUnit就可以轻松的完成测试工作,但是对于关联到其它比较复杂的类或对运行环境有要求的类...

    软件测试资料集合,jtest,jmock

    本资源包包含了多个与单元测试相关的工具和框架,特别是与`jtest`和`jmock`相关的材料。 **jtest** `jtest`是一款强大的单元测试工具,特别适合Java开发者。它提供了一个集成的测试环境,帮助开发者编写、运行和...

    jMock基本使用方法

    jMock 是一个流行的 Java 框架,用于支持单元测试,特别是对于模拟对象(mock objects)的支持。通过 jMock,开发人员可以轻松地创建模拟对象来替代实际的对象依赖,从而在单元测试中避免与外部系统的交互,确保测试...

    jmock-2.6.0-jars

    `jmock`是Java平台上的一个模拟框架,它允许开发者在单元测试中创建和控制对象的行为,以便能够隔离测试并精确地指定期望的交互。下面我们将深入探讨`jmock`的关键概念和功能。 ### 1. 模拟框架概述 `jmock`属于...

    jmock-2.5.1-javadoc

    《jMock 2.5.1:模拟对象与单元测试的艺术》 jMock是一个强大的Java模拟框架,它在软件开发的单元测试阶段扮演着至关重要的角色。jMock 2.5.1是该框架的一个版本,它提供了丰富的功能,帮助开发者创建虚拟对象,...

    Jmock2.6 jar包

    **Jmock2.6 Jar包**是Java编程领域中用于单元测试的重要库,它提供了模拟对象的功能,使得开发者能够在测试代码时对复杂依赖关系进行控制和隔离。在本文中,我们将深入探讨Jmock2.6的核心特性、使用场景以及如何在...

    jmock2.5.1.zip

    总结来说,jMock 2.5.1是一个强大的模拟工具,它使开发者能够在单元测试中有效地控制和验证对象行为,提高测试覆盖率和代码质量。通过阅读jMock 2.5.1.chm的帮助文档,开发者可以深入理解并掌握这一工具,从而在实践...

    单元测试文档

    本文将深入探讨如何利用`jmock`这个强大的工具来编写高效的单元测试,以及它如何促进Test Driven Development(TDD)和敏捷开发。 `jmock`是一个Java库,它提供了一种创建模拟对象(mock objects)的方法,这些模拟...

    jmock cookbook 资源整合

    JMock是一个用于Java应用程序单元测试的工具,它允许程序员模拟对象的行为,以便在孤立的环境中测试代码。在这个资源包中,包含了英文版的《JMock_CookBook_EN.pdf》和中文版的《JMock_CookBook_CN.pdf》,为不同...

    jmock jar包及doc文档

    本文将深入探讨JMock库,它是一个强大的Java模拟框架,常用于单元测试,以及如何结合Spring测试模块进行高效测试。 JMock是一个用于创建和验证对象行为的模拟框架,它允许开发者在测试中模拟对象的行为,而不是依赖...

    jMock Cookbook 中文版.doc

    在本文档《jMock Cookbook 中文版》中,主要探讨了如何使用jMock进行单元测试,特别是在Java的TDD(Test-Driven Development)实践中。jMock是一个强大的模拟框架,允许开发者在测试过程中模拟对象的行为,以便更好...

    JMOCK 帮助 网页 文档

    JMock 是一个Java平台上的单元测试框架,专门用于模拟对象,以便在测试过程中控制和验证对象的行为。它基于EasyMock库,但提供了更强大的功能,尤其是对于处理复杂交互和顺序的场景。JMock使得开发者可以在不依赖...

    jmock-1.2.0-jars.zip

    总之,JMock是Java开发者进行单元测试的重要工具,它通过模拟对象的创建和行为控制,帮助我们编写出更健壮、更可靠的代码。结合jmock-core和jmock-cglib这两个库,我们可以应对各种复杂的测试场景,提升测试的质量和...

    jmock2.5.1和easymock3.0

    而JMock和EasyMock则是两种广泛使用的Java单元测试框架,它们允许开发者模拟对象的行为和交互,以便于测试复杂的系统。本资源包含JMock 2.5.1和EasyMock 3.0的库文件,使得开发者在进行单元测试时无需再四处寻找相关...

    JMOCK-2.6.0

    《JMOCK-2.6.0:强化Java单元测试的艺术》 在软件开发的世界里,测试是确保代码质量不可或缺的一环。而JMOCK,作为一款强大的Java模拟框架,正是为了解决这一问题应运而生。"JMOCK-2.6.0"是一个专门用于项目测试的...

    JMock

    JMock 是一个流行的开源测试框架,专为Java开发者设计,用于模拟对象的行为,特别是在单元测试中。它允许程序员在不依赖真实对象的情况下测试代码,提高了测试的效率和隔离性。这篇详尽的讨论将深入JMock的核心概念...

    jmock学习文档

    jMock 是一个专门为 Java 设计的模拟对象框架,它的核心目标是帮助开发者在进行单元测试时,能够方便地创建和控制模拟对象,以替代真实的依赖,这样可以避免在测试环境中引入不必要的复杂性和依赖性。jMock 与 JUnit...

Global site tag (gtag.js) - Google Analytics