一、单元測试是保证软件质量的重要方法。

单元測试是对系统中某个模块功能的验证,但我们总会遇到这样那样的问题,导致測试代码非常难编写。最直接的一个原因便是强耦合关系,被測试者依赖一些不easy构造,比較复杂的对象,如:假设要測试一个servlet,我们必须获得HttpServletRequest,甚至须要一个Web容器;假设要測试Dao层,我们可能要获得JDBC相关对象,终于获得ResultSet。这些对象的构建并不那么easy,假设我们使用Mock方法(常见的一种单元測试技术,它的主要作用是模拟一些在应用中不easy构造或者比較复杂的对象,从而把測试与測试边界以外的对象隔离开),编写自己定义Mock对象是能够解决这个问题,但引入额外复杂代码的同一时候,非常easy引入额外的错误。

二、发现的源动力就是不将就!

面对上述问题,有非常多开源项目对动态构建 Mock 对象提供了支持,这些项目可以依据现有的接口或类动态生成Mock对象,这样不仅能避免额外的编码工作,同一时候也减少了引入错误的可能。

EasyMock 是一套用于通过简单的方法对于给定的接口生成 Mock 对象的类库。它提供对接口的模拟,能够通过录制、回放、检查三步来完毕大体的測试过程,能够验证方法的调用种类、次数、顺序,能够令 Mock 对象返回指定的值或抛出指定异常。通过 EasyMock,我们能够方便的构造 Mock 对象从而使单元測试顺利进行。

三、使用EasyMock完毕单元測试的过程大致能够划分为下面几个步骤:

  • 1、使用 EasyMock 生成 Mock 对象;
  • 2、设定 Mock 对象的预期行为和输出;
  • 3、将 Mock 对象切换到 Replay 状态;
  • 4、调用 Mock 对象方法进行单元測试;
  • 5、对 Mock 对象的行为进行验证。

四、文字表达有时候是苍白的,想不通过代码说事,还不行,看样子离大师还是有一段距离的。

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*; public class LoginServlet extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String username = request.getParameter("username");
String password = request.getParameter("password");
// check username & password:
if("admin".equals(username) && "123456".equals(password)) {
ServletContext context = getServletContext();
RequestDispatcher dispatcher = context.getNamedDispatcher("dispatcher");
dispatcher.forward(request, response);
}
else {
throw new RuntimeException("Login failed.");
}
} }

这个Servlet实现简单的用户验证的功能,若username和口令匹配“admin”和“123456”,则请求被转发到指定的dispatcher上,否则,直接抛出RuntimeException。

为了測试doPost()方法,我们须要模拟HttpServletRequest,ServletContext和RequestDispatcher对象,以便脱离J2EE容器来測试这个Servlet。

完整的LoginServletTest代码例如以下:

import javax.servlet.*;
import javax.servlet.http.*;
import org.easymock.*; public class LoginServletTest {
// 測试登陆失败
@Test
public void testLoginFailed() throws Exception {
// 使用 EasyMock 生成 Mock 对象;
MockControl mc = MockControl.createControl(HttpServletRequest.class);
HttpServletRequest request = (HttpServletRequest)mc.getMock();
// 设定 Mock 对象的预期行为和输出;
request.getParameter("username");
mc.setReturnValue("admin", 1);
request.getParameter("password");
mc.setReturnValue("1234", 1);
// 将 Mock 对象切换到 Replay 状态;
mc.replay();
// now start test:
LoginServlet servlet = new LoginServlet();
try {
// 里面会调用 Mock 对象方法进行单元測试;
servlet.doPost(request, null);
fail("Not caught exception!");
}
catch(RuntimeException re) {
assertEquals("Login failed.", re.getMessage());
}
// 对 Mock 对象的行为进行验证。
mc.verify();
}
 
    // 測试登陆成功
@Test
public void testLoginOK() throws Exception {
// 使用 EasyMock 生成 Mock 对象;
MockControl requestCtrl = MockControl.createControl(HttpServletRequest.class);
HttpServletRequest requestObj = (HttpServletRequest)requestCtrl.getMock();
MockControl contextCtrl = MockControl.createControl(ServletContext.class);
final ServletContext contextObj = (ServletContext)contextCtrl.getMock();
MockControl dispatcherCtrl = MockControl.createControl(RequestDispatcher.class);
RequestDispatcher dispatcherObj = (RequestDispatcher)dispatcherCtrl.getMock();
// 设定 Mock 对象的预期行为和输出;
requestObj.getParameter("username");
requestCtrl.setReturnValue("admin", 1);
requestObj.getParameter("password");
requestCtrl.setReturnValue("123456", 1);
contextObj.getNamedDispatcher("dispatcher");
contextCtrl.setReturnValue(dispatcherObj, 1);
dispatcherObj.forward(requestObj, null);
dispatcherCtrl.setVoidCallable(1);
// 将 Mock 对象切换到 Replay 状态;
requestCtrl.replay();
contextCtrl.replay();
dispatcherCtrl.replay();
// 里面会调用 Mock 对象方法进行单元測试;
//为了让getServletContext()方法返回我们创建的ServletContext Mock对象,我们定义一个匿名类并覆写getServletContext()方法:
LoginServlet servlet = new LoginServlet() {
public ServletContext getServletContext() {
return contextObj;
}
};
servlet.doPost(requestObj, null);
// 对 Mock 对象的行为进行验证。
requestCtrl.verify();
contextCtrl.verify();
dispatcherCtrl.verify();
}
}

五、总结

EasyMock 推荐依据指定接口动态构建 Mock 对象,这促使我们遵循“面向接口编程”的原则:假设不面向接口,则測试难于进行。是否easy进行单元測试也体现了代码质量的高低,难以測试的代码,通常也是充满坏味道的代码。能够这么说,假设代码在单元測试中难于应用,则它在真实环境中也将难于应用。总之,创建尽可能easy測试的代码就是创建高质量的代码。

聊聊单元測试(一)——EasyMock的更多相关文章

  1. 利用Continuous Testing实现Eclipse环境自己主动单元測试

    当你Eclipse环境中改动项目中的某个方法时,你可能因为各种原因没有执行单元測试,结果代码提交,悲剧就可能随之而来. 所幸infinitest(http://infinitest.github.io ...

  2. 在Eclipse中使用JUnit4进行单元測试(0基础篇)

    本文绝大部分内容引自这篇文章: http://www.devx.com/Java/Article/31983/0/page/1 我们在编写大型程序的时候,须要写成千上万个方法或函数,这些函数的功能可能 ...

  3. C语言单元測试

    C语言单元測试 对于敏捷开发来说,单元測试不可缺少,对于Java开发来说,JUnit非常好,对于C++开发,也有CPPUnit可供使用,而对于传统的C语言开发,就没有非常好的工具可供使用,能够找到的有 ...

  4. OpenStack中给wsgi程序写单元測试的方法

    在 OpenStack 中, 针对web应用, 有三种方法来写单元測试 1) 使用webob生成模拟的request from __future__ import print_function imp ...

  5. Android单元測试之JUnit

    随着近期几年測试方面的工作慢慢火热起来.常常看见有招聘測试project师的招聘信息.在Java中有单元測试这么一个JUnit 方式,Android眼下主要编写的语言是Java,所以在Android开 ...

  6. 让你提前认识软件开发(19):C语言中的协议及单元測试演示样例

    第1部分 又一次认识C语言 C语言中的协议及单元測试演示样例 [文章摘要] 在实际的软件开发项目中.常常要实现多个模块之间的通信.这就须要大家约定好相互之间的通信协议,各自依照协议来收发和解析消息. ...

  7. php单元測试

    你是否在程序开发的过程中遇到下面的情况:当你花了非常长的时间开发一个应用后,你觉得应该是大功告成了,可惜在调试的时候,老是不断的发现bug,并且最可怕的是,这些bug是反复出现的,你可能发现这些bug ...

  8. Android 进行单元測试难在哪-part3

    原文链接 : HOW TO MAKE OUR ANDROID APPS UNIT TESTABLE (PT. 1) 原文作者 : Matthew Dupree 译文出自 : 开发技术前线 www.de ...

  9. iOS 单元測试之XCTest具体解释(一)

    原创blog,转载请注明出处 blog.csdn.net/hello_hwc 欢迎关注我的iOS-SDK具体解释专栏 http://blog.csdn.net/column/details/huang ...

随机推荐

  1. Python计算&绘图——曲线拟合问题(转)

    题目来自老师的课后作业,如下所示.很多地方应该可以直接调用函数,但是初学Python,对里面的函数还不是很了解,顺便带着学习的态度,尽量自己动手code. 测试版代码,里面带有很多注释和测试代码: # ...

  2. [ACM] POJ 3686 The Windy's (二分图最小权匹配,KM算法,特殊建图)

    The Windy's Time Limit: 5000MS   Memory Limit: 65536K Total Submissions: 4158   Accepted: 1777 Descr ...

  3. java--照片和BYTE这些东西阵列

    使用java,图像被变换成BYTE排列.和该阵列为图象,远程传输的图片进行 参考:http://blog.csdn.net/huang9012/article/details/18241539 代码例 ...

  4. 转让malloc()该功能后,发生了什么事内核?附malloc()和free()实现源

    特此声明:在本文中,引用另一篇文章和帖子,结合的概括的理解malloc()函数的实现机制. 我们常常会在C程序中调用malloc()函数动态分配一块连续的内存空间并使用它们.那么,这些用户空间发生的事 ...

  5. 【SSH2(实用文章)】--Struts2文件上传和下载的例子

    回想一下,再上一篇文章Struts2实现机制,该步骤做一步一步来解决,这种决心不仅要理清再次Struts2用法.映射机制及其在深入分析.最后一个例子来介绍Struts2一种用法,这里将做一个有关文件上 ...

  6. DDD事件总线

    DDD事件总线 基本思路: (1)       在事件总线内部维护着一个事件与事件处理程序相映射的字典. (2)       利用反射,事件总线会将实现了IEventHandler的处理程序与相应事件 ...

  7. -ms-grid -ms-grid-rows -ms-grid-row -ms-grid-columns -ms-grid-column

    style: display:-ms-grid-ms-grid-columns和-ms-grid-rows的值可以为: >标准长度单位,如像素 >对象宽度(对于列)或高度(对于行)的百分比 ...

  8. Android MotionEvent事故响应机制

    于android于.主要活动包括点击.按.拖累.滑动等操作,这些构成了Android事件响应,总体而言,,所有事件由例如以下三部分构成的基础: 按(action_down),搬家(action_mov ...

  9. 据序和中序序列或者也许为了一个二进制序列,恢复二进制和打印图像(c语言)

    首先要预购和序,以恢复它: 1.首先,我们使用的是递归的方式来完成 2.递归的最小单位:一个空的树和书的前言和第一序.该序列的第一个元素是树的第一序列根,调用这种方法 3.递归的终止条件是.当这棵树的 ...

  10. 一个简单的Java死锁示例(转)

    在实际编程中,要尽量避免出现死锁的情况,但是让你故意写一个死锁的程序时似乎也不太简单(有公司会出这样的面试题),以下是一个简单的死锁例子,程序说明都写着类的注释里了,有点罗嗦,但是应该也还是表述清楚了 ...