惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

量子位
Stack Overflow Blog
Stack Overflow Blog
人人都是产品经理
人人都是产品经理
The GitHub Blog
The GitHub Blog
Engineering at Meta
Engineering at Meta
Vercel News
Vercel News
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Y
Y Combinator Blog
The Cloudflare Blog
Last Week in AI
Last Week in AI
B
Blog
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
T
Tailwind CSS Blog
V
Visual Studio Blog
博客园 - 三生石上(FineUI控件)
小众软件
小众软件
Google DeepMind News
Google DeepMind News
D
DataBreaches.Net
博客园 - 司徒正美
B
Blog RSS Feed
Microsoft Azure Blog
Microsoft Azure Blog
罗磊的独立博客
Hugging Face - Blog
Hugging Face - Blog
L
LangChain Blog

博客园 - eafy.ye

Java获取系统信息(cpu,内存,硬盘,进程等)的相关方法 url参数中传递中文 - eafy.ye - 博客园 cglib介绍(转) openfire消息包接受处理流程&Openfire Plugins加载流程 eclipse中开发openfire 垃圾收集器与Java编程(转) Struts2的工作机制及分析(转) Freemarker caching of BeanWrapper models java传递是引用的拷贝,既不是引用本身,更不是对象 open session and Hibernate事务处理机制 Hibernate/JPA中的继承映射 奥运会开幕 Acegi集成CAS hibernate annotations和hbm.xml配置文件在spring中的并存配置 jBPM-jPDL学习笔记——流程设计与控制(转) jBPM开发入门指南 JAVA动态代理实现&&动态字节码生成(asm) java与Groovy的整合 Dom4j 使用简介
JMock来实现孤立测试(转)
eafy.ye · 2008-07-03 · via 博客园 - eafy.ye

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);
    }
}

package jmock;
import static org.testng.AssertJUnit.*;
import org.jmock.Expectations;
import org.jmock.Mockery;
import org.testng.annotations.BeforeSuite;
import org.testng.annotations.Test;
 
public class UserServiceTest {
 
 Mockery context = new Mockery();
 UserDao userDao = null;
 private UserService userService = new UserServiceImp();
 
 @BeforeSuite
 protected void setUp() {   
  userDao = context.mock(UserDao.class);   
  userService.setUserDao(userDao);
    }   
 
 @Test
 public void testGetUser() {
        final User fakeUser = new User("John");
       
        //expectations
        context.checking(new Expectations() {{
            one (userDao).getUser(1l);
            will (returnValue(fakeUser));
        }});              
       
        User user = userService.getUser(1L);
        assertNotNull(user);
        assertEquals("John", user.getName());
    }
 
 @Test
 public void testSaveUser(){
  final User fakeUser = new User("John");
       
        //expectations
  context.checking(new Expectations() {{
            one (userDao).getUser(1l);
            will (returnValue(fakeUser));
        }});              
       
        final User user = userService.getUser(1L);
        assertEquals("John", user.getName());
       
      
      //expectations
  context.checking(new Expectations() {{
            one (userDao).saveUser(fakeUser);           
        }});
  
   user.setName("eafy");
   userService.saveUser(user);
  
  //expectations
  context.checking(new Expectations() {{
             one (userDao).getUser(1l);
             will (returnValue(user));
     }});
  
  User modifiedUser = userService.getUser(1L);
        assertEquals("eafy", modifiedUser.getName());       
        
 }
}

    此段代码有几点应注意:

1、此测试类采用testng

2、Mockery context = new Mockery();jmock2.0 采用Mockery
3、在setup()中,将userDAO.class传入context.Mock()后
    通过以上实例代码及其说明,我们看出,用好JMock的关键是先设置监控条件,再写相应的测试语句。一旦设好监控条件后,在某段代码块执行完毕时,如果监控条件未得到满足,或是没有通过expects()再次重设条件,测试将无法通过。
4、context.checking(new Expectations() {{
            one (userDao).getUser(1l);
            will (returnValue(fakeUser));
        }});表示测试一次,返回User对象
5、testSaveUser中首先保存一个用户对象,再修改属性,再判断修改后的值,是否正确


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

(做了部分修改)