`
wujun27
  • 浏览: 13344 次
  • 性别: Icon_minigender_1
  • 来自: 上海
最近访客 更多访客>>
社区版块
存档分类
最新评论

JMock的基本使用方法

 
阅读更多
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基本使用方法 #### 一、简介 jMock 是一个流行的 Java 框架,用于支持单元测试,特别是对于模拟对象(mock objects)的支持。通过 jMock,开发人员可以轻松地创建模拟对象来替代实际的对象依赖,从而在...

    JMOCK使用文档

    本文档将详细介绍JMock的使用方法及其与JUnit的结合。 1. **什么是JMock?** JMock是一个开源的Java库,它允许开发者在测试中创建和控制对象的行为。通过模拟对象,我们可以隔离被测试代码,专注于测试单个组件的...

    JMock

    ### 基本使用 在JMock中,模拟对象通过`mock(Class<T> clazz)`方法创建,然后可以使用`expect`方法来指定其行为。例如,如果你有一个`Calculator`接口,可以这样创建并设置模拟对象: ```java Mockery context = ...

    jmock-2.5.1-javadoc

    除了基本的模拟和期待,jMock还支持更复杂的场景,如顺序(顺序调用模拟对象的方法)、条件(某些方法的调用依赖于其他方法的调用结果)和回调(模拟对象方法的返回值取决于其他方法的调用)等。这些特性使得jMock...

    jmock-1.2.0-jars.zip

    JMock的使用方法主要包括以下几个步骤: 1. **创建模拟对象**:使用`Mockery`类来创建模拟对象,如`Mockery context = new Mockery(); MyInterface mock = context.mock(MyInterface.class);` 2. **定义行为**:...

    jmock cookbook 资源整合

    《JMock Cookbook资源整合》是关于Java测试领域中JMock库使用的详细指南,旨在帮助开发者更好地理解和运用这个强大的模拟框架。JMock是一个用于Java应用程序单元测试的工具,它允许程序员模拟对象的行为,以便在孤立...

    使用 FactoryBean结合Jmock实现动态Mock类的注入

    为了结合`FactoryBean`和Jmock,我们需要在`FactoryBean`的实现中使用Jmock来动态创建模拟对象。以下是一个简单的示例: ```java public class MockingFactoryBean implements FactoryBean<Object> { private ...

    maven+jmock

    接下来,我们需要了解 JMock 的基本概念。JMock 提供了模拟(mock)、期待(expectation)和验证(verification)等机制。模拟对象可以代替真实的对象,提供预定义的返回值或行为。期待定义了在测试过程中模拟对象...

    Jmock Mock 没有接口的类

    以下是如何使用 JMock 和 CGLIB 模拟无接口类的基本步骤: 1. 引入依赖:将这些 jar 文件添加到项目的类路径中,或者在使用 Maven 或 Gradle 的项目中,添加相应的依赖配置。 2. 创建模拟对象:使用 JMock 的 `...

    JMock基本概念及实现[定义].pdf

    JMock 是一种在Java开发中用于创建和使用Mock对象的框架,主要应用于单元测试中,以解决依赖于复杂外部资源或行为的代码测试难题。Mock对象是真实对象的替代品,它模拟了真实对象的行为,使得测试可以在不受外界影响...

    JMock单元测试

    1. JMock的基本使用 首先,要在项目中使用JMock,必须引入几个核心的jar包:jjmock.jar、jjmock-junit.jar、jjunit.jar和hamcrest-core.jar,以及hamcrest-library.jar。这些jar包是运行JMock测试的必要条件。 2. ...

    jmock-2.6.0-RC2-jars.rar

    例如,可以使用jMock定义模拟对象的期望,然后在JUnit的测试方法中执行这些期望并验证结果。 总结来说,jMock 2.6.0-RC2是Java单元测试的重要工具,其强大的模拟对象功能和改进的API为开发者提供了更高的测试灵活性...

    单元测试与 Mock 方法

    除了EasyMock,还有其他类似的库,如Mockito和JMock,它们提供了更高级的功能,如更加灵活的匹配器和更易于使用的API。 在进行Mock方法时,我们还需要注意几个关键点: - 保持Mock对象的简洁性:尽量只模拟必要的...

    自动化测试示例TestDemo完善最新版本

    TestNG则是另一个强大的测试框架,相比JUnit,它提供了更多的功能,如并发测试、参数化测试、配置方法、测试分组等。TestNG的报告功能也更加强大,能提供详细的测试结果分析。本示例项目最新版本增加了对TestNG的...

    单元测试一条龙

    - 计划更新的内容包括Firefox 10下的Selenium-IDE,Jmock桩的规范使用,Ant自动构建,Cobertura覆盖率计算,以及TestNG的介绍。 8. **文档来源与贡献**: - 文档主要由云层编写,同时得到了学生和技术社区的支持...

Global site tag (gtag.js) - Google Analytics