聊聊单元測试(一)——EasyMock
一、单元測试是保证软件质量的重要方法。
单元測试是对系统中某个模块功能的验证,但我们总会遇到这样那样的问题,导致測试代码非常难编写。最直接的一个原因便是强耦合关系,被測试者依赖一些不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的更多相关文章
- 利用Continuous Testing实现Eclipse环境自己主动单元測试
当你Eclipse环境中改动项目中的某个方法时,你可能因为各种原因没有执行单元測试,结果代码提交,悲剧就可能随之而来. 所幸infinitest(http://infinitest.github.io ...
- 在Eclipse中使用JUnit4进行单元測试(0基础篇)
本文绝大部分内容引自这篇文章: http://www.devx.com/Java/Article/31983/0/page/1 我们在编写大型程序的时候,须要写成千上万个方法或函数,这些函数的功能可能 ...
- C语言单元測试
C语言单元測试 对于敏捷开发来说,单元測试不可缺少,对于Java开发来说,JUnit非常好,对于C++开发,也有CPPUnit可供使用,而对于传统的C语言开发,就没有非常好的工具可供使用,能够找到的有 ...
- OpenStack中给wsgi程序写单元測试的方法
在 OpenStack 中, 针对web应用, 有三种方法来写单元測试 1) 使用webob生成模拟的request from __future__ import print_function imp ...
- Android单元測试之JUnit
随着近期几年測试方面的工作慢慢火热起来.常常看见有招聘測试project师的招聘信息.在Java中有单元測试这么一个JUnit 方式,Android眼下主要编写的语言是Java,所以在Android开 ...
- 让你提前认识软件开发(19):C语言中的协议及单元測试演示样例
第1部分 又一次认识C语言 C语言中的协议及单元測试演示样例 [文章摘要] 在实际的软件开发项目中.常常要实现多个模块之间的通信.这就须要大家约定好相互之间的通信协议,各自依照协议来收发和解析消息. ...
- php单元測试
你是否在程序开发的过程中遇到下面的情况:当你花了非常长的时间开发一个应用后,你觉得应该是大功告成了,可惜在调试的时候,老是不断的发现bug,并且最可怕的是,这些bug是反复出现的,你可能发现这些bug ...
- Android 进行单元測试难在哪-part3
原文链接 : HOW TO MAKE OUR ANDROID APPS UNIT TESTABLE (PT. 1) 原文作者 : Matthew Dupree 译文出自 : 开发技术前线 www.de ...
- iOS 单元測试之XCTest具体解释(一)
原创blog,转载请注明出处 blog.csdn.net/hello_hwc 欢迎关注我的iOS-SDK具体解释专栏 http://blog.csdn.net/column/details/huang ...
随机推荐
- 【Android进阶】Activity和Fragement中onSaveInstanceState()的使用详解
在activity(或者是fragement)被杀掉之前调用保存每个实例的状态,以保证该状态可以在onCreate(Bundle)或者onRestoreInstanceState(Bundle) (传 ...
- Codeforces 437E The Child and Polygon(间隔DP)
题目链接:Codeforces 437E The Child and Polygon 题目大意:给出一个多边形,问说有多少种切割方法.将多边形切割为多个三角形. 解题思路:首先要理解向量叉积的性质,一 ...
- windows-install-python-and-sphinx(*.rst file)
http://sphinx-doc.org/install.html#windows-install-python-and-sphinx
- 在SQL2008中,如何让id自动生成并自动递增?如何让时间默认生成?
id自动递增: 如果是用语句操作,这样定义:ID INT IDENTITY,如果是要生成一对数字,这样定义:ID INT IDENTITY(1,1) 如果要在SQL Server的表中设置 ...
- 在python正在使用mysql
缘由 近期在折腾一个小东西须要抓取网上的页面.然后进行解析.将结果放到数据库中. 了解到Python在这方面有优势,便选用之. 由于我有台server上面安装有mysql,自然使用之.在进行数据库的这 ...
- Notes系统安全日志
最近出现了一些不正常的操作系统,但到目前为止,只有一个粗略的验证,然而,彻底清除.对于安全日志系统中的一些总结. 1.系统登录必须先登录 该日志必须包含:登录时间.注销时间.登录ip.主机名.MAC住 ...
- OpenGL路(四)自制的图形功能(立方体、汽缸、圆锥)
#include <gl/glut.h> #include <gl/GLU.h> #include <gl/GL.h> #pragma comment(lib, & ...
- 页面中插入百度地图(使用百度地图API)
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQveWF5dW4wNTE2/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA ...
- dom 规划(html和xml)
html dom与xml dom关联: 什么是 DOM? DOM 是 W3C(万维网联盟)的标准. DOM 定义了訪问 HTML 和 XML 文档的标准: "W3C 文档对象模型 (DOM) ...
- ios学习网络------4 UIWebView以三种方式中的本地数据
UIWebView这是IOS内置的浏览器.能够浏览网页,打开文档 html/htm pdf docx txt等待格文档类型. safari浏览器是通过UIWebView制作. server将 ...