深入浅出学测试——(三)

  1. 模拟对象

从实现角度而言,模拟对象更加复杂。模拟对象可以验证待测对象与其协作对象的交互。由于具体实现方式不同,有些模拟对象可以返回硬编码的值,而有些可以提供逻辑的伪实现。模拟对象通常由框架或类库(如EasyMock、JMock)动态产生,不过也可以手工实现。

从前面的讲述中可以知道,使用JUnit4进行单元测试的时候,被测试代码需要和它所调用的代码隔离开。那么调用谁呢,用JMock获得的模拟对象就是最方便的选择。Mock方法是单元测试中常用的一种技术,它的主要作用是模拟一些在应用中不容易构造或者比较复杂的对象,例如Servlet、ResultSet、HttpServletRequest、HttpServletResponse、HttpSession等,从而把测试与测试边界以外的对象隔离开。

1、模拟对象JMock—模拟对象的意义

JMock简介

JMock是写单元测试时需要生成mock对象时很好的辅助库。

将jmock-2.5.1.jar和jmock-junit4-2.5.1.jar添加到工程的classpath。

测试用例:

//表示使用JMock运行测试用例

package com.jhaso.main;

 

import org.jmock.integration.junit4.JMock;

import org.junit.runner.RunWith;

 

@RunWith(JMock.class)

public class JMockTest {

   

}

 

   

 

 

 

 

 

 

 

 

 

 


2、模拟对象JMock—JMock准备

Mock对象上下文:Mockery类代表着所测试对象与之交互的测试环境.

3、模拟对象JMock—获得模拟对象

根据期望值测试(Tests with Expectations)

                                                    

import junit.framework.Assert;

import org.jmock.Expectations;

import org.jmock.Mockery;

import org.jmock.integration.junit4.JMock;

import     org.jmock.integration.junit4.JUnit4Mockery;

import org.junit.Test;

import org.junit.runner.RunWith;

import com.jhaso.service.UserEbo;

 

/**

 * @author 铁臂金刀

*/

@RunWith(JMock.class)

public class TestUserEbo     {

    //模拟接口

    Mockery context = new JUnit4Mockery();

   

    /**

     *     JMock的用法

     *     1.期望值

     *     2.真实值

     *     在调用被测试方法之前,需要把mockdao放进去,它才能工作

     *     3.用断言比较

     */

    @Test

    public void test1(){

       //模拟对象:

       final UserDao userDao= context.mock(UserDao.class);

       //什么方法在什么情况下,返回什么值

      

       context.checking(new Expectations(){

           {

              //设置被调用的次数

              oneOf(userDao).login("jhaso","jhaso");

              //方法返回值

              will(returnValue("ok"));

           }

       });

      

 

 

       UserEbo ebo = new UserEbo();

       ebo.setDao(userDao);

       final boolean expected = true;

       final boolean actual = ebo.login("jhaso", "jhaso");

       Assert.assertEquals(expected, actual);

    }

}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 


【注意】:({});两个花括号,仍然是使用匿名内部类,外面那对{}的作用相当于方法的开始与结尾;里面那对{}是子类的构造方法

 

4、模拟对象JMock—根据期望测试

invocation-count(mock-object).method(argument-constraints);   

inSequence(sequence-name);

when(state-machine.is(state-name));

will(action);

then(state-machine.is(new-state-name));

invocation-count 调用的次数约束

mock-object mock对象

method 方法

argument-constraints 参数约束

inSequence顺序

when mockery的状态机

will(action) 方法触发的动作

then 方法触发后设置mockery的状态

5、模拟对象JMock—JMock语法

//具体相等

allowing(dao).sayHello("ok");

//具体相等

allowing(dao).sayHello(with(equal("ok")));

//字符串包含

allowing(dao).sayHello(with(newStringContains("ok")));

//==判断

allowing(dao).sayHello(with(same("ok")));

//String类型的null

allowing(dao).sayHello(with(aNull(String.class)));

//String类型,但是不是null

allowing(dao).sayHello(with(aNonNull(String.class)));

//String类型的任何东西,可以包含null

allowing(dao).sayHello(with(any(String.class)));

6、模拟对象Jmock的返回值

//模拟对象JMock—调用的次数约束

返回值:oneOf(anObject).doSomething(); will(returnValue(10));

在连续调用中返回不同值:

oneOf(anObject).doSomething(); will(returnValue(10));

oneOf(anObject).doSomething(); will(returnValue(20));

oneOf(anObject).doSomething(); will(returnValue(30));

第一次调用doSomething会返回10,第二次返回20,第三次返回30.

从模拟方法抛出异常:

allowing (bank).withdraw(with(any(Money.class)));

will(throwException(newWithdrawalLimitReachedException());

7、模拟对象JMock—方法返回值

不同参数值返回不同结果:

allowing (calculator).add(1,1); will(returnValue(3));

allowing (calculator).add(2,2); will(returnValue(5));

allowing (calculator).sqrt(-1); will(throwException(newException());

 

讲了使用JMock获得的模拟对象,下面再讲讲使用EasyMock获得的模拟对象。

1、EasyMock提供了根据指定接口动态构建Mock对象的方法,避免了手工编写Mock对象。EasyMock是一套通过简单的方法对于指定的接口或类生成Mock(模拟)对象的类库,它能利用对接口或类的模拟来辅助单元测试。它提供对接口的模拟,能够通过录制、回放、检查3步来完成测试过程,也可以验证方法的调用种类、次数、顺序,它可以想Jmock一样令Mock对象返回指定的值或抛出指定的异常,通过EasyMock,我们也可以方便地构造Mock对象从而使单元测试顺利进行。

将easymock.jar添加到工程的classpath中即可。如果是Maven项目可在pom.xml中添加一下代码:

<dependency>

    <groupId>org.easymock</groupId>

    <artifactId>easymock</artifactId>

    <version>3.4</version>

</dependency>

    EasyMock 采用“记录/回放”的工作模式,基本使用步骤如下:

  • 创建Mock对象的控制对象Control。

  • 从控制对象中获取所需要的Mock对象。

  • 记录测试方法中所使用到的方法和返回值。

  • 设置Control对象到“回放”模式。

  • 进行测试。

  • 在测试完毕后,确认Mock对象已经执行了刚才定义的所有操作。

下面我们以HttpServlet为例,来编写一个登陆Servlet类,它从request对象中取出用户名和秘密,然后进行登陆验证。代码如下:

 

 

 

public class LoginServlet     extends HttpServlet{

    public void doPost(HttpServletResponse response,HttpServletRequest     request) throws ServletException,IOException{

       String userName = request.getParameter("userName");

       String passWord = request.getParameter("passWord");

       System.out.println("用戶名 : " + userName);

       System.out.println("秘密 : " + passWord);

    }

}

 

   

 

 

 

 

 

 

 

 

 


使用EasyMock模拟创建request和response对象,并可以模拟他们的属性、方法、及返回值。这样就可以通过EasyMock来辅助完成单元测试了。

 1)创建HttpServletRequest模拟对象。

 2)设置HttpServletRequest对象的模拟属性和方法。

 3)设置回访模式。

 4)创建LoginServlet实例,并使用模拟的request执行测试。

 5)调用verify()执行验证。

 

测试代码如下

package com.jhaso.servlet;

 

import java.io.IOException;

import javax.servlet.ServletException;

import javax.servlet.http.HttpServletRequest;

import junit.framework.TestCase;

import org.easymock.EasyMock;

public class     LoginServletTest extends TestCase {

    /**

     *    

     *     @throws IOException

     *     @throws ServletException

     */

    public void testDoPost() throws     IOException,ServletException{

       /**

        * 1)创建HttpServletRequest模拟对象。

        * 2)设置HttpServletRequest对象的模拟属性和方法

        * 3)设置回访模式

        * 4)创建LoginServlet实例,并使用模拟的request执行测试

        * 5)调用verify()执行验证

        */

       //获取Mock對象

       HttpServletRequest request = EasyMock.createMock(HttpServletRequest.class);

          

      

 

//模拟request.getParameter()的值

 

EasyMock.expect(request.getParameter("userName")).andReturn("admin");

     EasyMock.expect(request.getParameter("passWord")).andReturn("admin");

       //設置回访模式

       EasyMock.replay(request);

       //进行测试

LoginServlet servlet = new LoginServlet();

       servlet.doPost(null, request);

       //在调用的模拟对象前,一定要执行verify操作

       EasyMock.verify(request);

    }

}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 


    其中最重要的是使用expect()来设置模拟对象request的属性,它还包含以下的模拟函数。

  • expectAndReturn(): 设置期望调用的函数及返回值。

  • expectAndThrow():  设置期望调用的函数,同时期望该次调用抛出异常。

  • setReturnValue():  设置上一次调用的返回值。

  • setThrowable():    设置上次调用抛出的异常。

你可能感兴趣的:(深入浅出学测试——(三))