??xml version="1.0" encoding="utf-8" standalone="yes"?> 手头的项目越来越大,很多以前不会出现的问题开始Q现?/p> 比如Q我修改了一个基的类库,却意外的影响了九重天外的客户目Q直接导致一个功能无法实现。我郁闷啊!Q! 因此开始要有组l、有预谋、有计划的对目q程q行试驱动了。最l目标是Q我修改了底层某个dll的某个方法,试框架能够自动帮我扑և来所有收到媄响的c,全部执行一ơ回归测试,q发送一份漂亮的报告到我手里?/p> q个目标估计1?个星期才能实玎ͼ不过现在先放Z个非常漂亮的MOCK核心代码?nbsp; 在不断收集各U资料过E中Q学习了很多Q例如以下关键字Q有兴趣的兄弟可以自己搜索一下: testdriven.net, nunit, typemock, cruiseControl.net, Confluence, JIRE, NUnitForms, WatiN, MBUnit, CSUnit, NBehave, Gallio ranorex, dynamicProxy... 估计各位有时间看看上面的介,p够掌握现在测试驱动的大致发展?/p> 接下来就说下Mock技术和试驱动中的作用?/p> 我最不喜Ƣ老外造名词,所以会用自q体会Ml?/p> mock的本质就是模拟目标对象的一个假对象?nbsp; q个性质在测试驱动中非常有用Q例如我的代码是Q?/p> 现在要测试这个代码,需要传入一个IOrmScheduleTrrigger的接口对象。但是不q的是,q个对象是个ORMQ要启动q个对象Q就涉及C数据库。。。?/p> 老大Q我只是x试一下一辆宝马的ȝ是否坚硬Q不需要启动我的宝马加速到120kmQ然后再用手ȝ那块玻璃吧?/p> 所以,我希望能够有个模拟对象,l承了这个接口, 同时提供了我期望的返回|让这个方法能够顺利执行?/p> 传统的傻逼方法,是自己写一个类Q承了q个接口Q然后才传入q去。例如: q样不就更加的傻gQ我Z一块玻璃,q亲自造了另外一台简易的宝马出来Q? 于是我开始翻阅各U文献,甚至考虑使用动态代理(DynamicProxyQ。动态代理的核心思想是在代码运行中写IL生成一个承类。这个技术很有用Q但是现在我q用不上Q就像martin fowler说的Qtypemockq于把核武器交l了一?岁小孩)?/p> 于是我l寻找,l于d了Moq的源码,扑ֈ了答案?/p> 先看看以下一D代码,是我摘自Moq源码的核心部分,E加攚w了Q?/p> q段代码是Moq的核心思想?/p> 大概意思是Q我希望调用接口IMock的方法DevideQ但是我压根不想写这个接口的实现?/p> 那么我先写一个通用的模拟对象Mock<TInterface>Q承了RealProxy?/p> 然后通过调用Value可以返回需要的接口对象。而这个对象就?nbsp;return (TInterface)this.GetTransparentProxy();是个透明代理?/p> 最后当我调用了 int Devide(int a, int b); Ҏ的时候,{于调用了public override IMessage Invoke(IMessage msg)ҎQ有点点的AOP感觉Q?/p> 上文是Moq的核心思想了。非常的_ֽQ估计有了思\Q各位就可以刉自q原子弹了?/p> q里插句题外话,很多人抨击重复造轮子。我奇怪了。如果我造一个轮子花费的旉和学习用一个轮子的旉差不q,Z么不造一个? 而且Q用别h的轮子,l常出现的情冉|Q很多轮子不知道挑哪个。一旦挑上了Q项目进展到一般才发现不适合、有bugQ于是又重头挑另外的轮子?/p> q个l历是真实的。当q读大学Q我的室友就是典型的挑轮子,他懂得很多框ӞjavaQ,webworkQhibernate, spring。和人砍h朗朗上口Q但是需要深入做目了,出现问题基本上不知所措,不是L献,是问师兄,最后整个项目组从来没有一个成品?/p> 我自从学电脑依赖Q从来就没有用过别h的轮子,即是hibernateQ我的确也没有用q,不过他的核心文档倒是看过Q对比之下,和oracle的toplink相比直就是小孩?/p> 比我牛逼的兄弟大有人在Q希望各位牛Z要浪费自q旉L别h的轮子,直接自己造一个算了?nbsp; 最后说说接下来的工作?/p> Z接口的测试驱动完成了Q剩下的是面对sealed class {顽固分子了Q?必然需要动用非常规武器QDynamicProxy。下回再见?/p> 1、什么情况下会用mock技?/span> Q?Q需要将当前被测单元和其依赖模块独立开来,构造一个独立的试环境Q不x被测单元的依赖对象,只关注被单元的功能逻辑 ----------比如被测代码中需要依赖第三方接口q回D行逻辑处理Q可能因为网l或?a style="line-height: normal !important; color: #333333; word-break: break-all; text-decoration: none" target="_self">其他环境因素Q调用第三方l常会中断或者失败,无法对被单元进行测试,q个时候就可以使用mock技术来被单元和依赖模块独立开来,使得试可以q行下去?/p> Q?Q被单元依赖的模块未开发完成,而被单元需要依赖模块的q回D行后l处?/p> ----------比如service层的代码中,包含对Dao层的调用Q但是,DAO层代码尚未实?/p> Q?Q被单元依赖的对象较难模拟或者构造比较复?/p> ----------比如Q支付宝支付的异常条件有很多Q但是模拟这U异常条件很复杂或者无法模拟,比如Q查询聚划算的订单结果,无法在测试环境进行模?/p> 2、Mock技术分c?/span> Q?Q手动构造mock对象 ---------------比如Q可以自己写某个接口Ҏ的实玎ͼҎ需要编写返回|试代码中用该实现cd?/p> ~点Q会增加代码量,在写mock对象代码Ӟ有可能引入错?/p> Q?Q用开源代码提供的构造mockҎ --------------比如easyMockQ提供了Ҏ口类的模拟,能够通过录制、回放、检查三步来完成大体的测试过E,可以验证Ҏ的调用种cR次数、顺序,可以令Mock对象q回指定的值或抛出指定异常 3、EasyMock使用 Q?Q引入easyMock ------------在maven工程中,通过pom配置依赖关系 ------------在普?a style="line-height: normal !important; color: #333333; word-break: break-all; text-decoration: none" target="_self">java工程中,通过d外部包的方式 Q?Q用easyMockq程 1Q用EasyMock生成Mock对象Q?br style="line-height: normal !important; word-break: break-all" /> pingJiaDao = mockControl.createMock(IPingJiaDao.class); 2Q设定Mock对象的预期行为和输出Q?br style="line-height: normal !important; word-break: break-all" /> EasyMock.expect(pingJiaDao.getGoodPingJiaRate(storeId)).andReturn(0.11); 3Q将Mock对象切换到Replay状态; 4Q调用Mock对象Ҏq行单元试Q?br style="line-height: normal !important; word-break: break-all" /> storeService.setStoredao(pingJiaDao); 5Q对Mock对象的行行验证?br style="line-height: normal !important; word-break: break-all" /> EasyMock.verify(pingJiaDao); 4、其他easyMock功能 Q?Q特D的mock对象QniceMock
package com.abin.lee.easymock.servlets;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* Created with IntelliJ IDEA.
* User: abin
* Date: 13-4-22
* Time: 下午3:12
* To change this template use File | Settings | File Templates.
*/
public class EasyMockServlet extends HttpServlet {
public void doPost(HttpServletRequest request,HttpServletResponse response) throws IOException, ServletException {
System.out.println("doPost come in");
String userName=request.getParameter("userName");
String passWord=request.getParameter("passWord");
System.out.println("userName="+userName+",passWord="+passWord);
if("abin".equals(userName)&&"varyall".equals(passWord)){
System.out.println("come in");
ServletContext context=this.getServletContext();
RequestDispatcher dispatcher=context.getNamedDispatcher("dispatcher");
dispatcher.forward(request,response);
}else{
throw new RuntimeException("Login failed.");
}
}
}
//EasyMockServletFailedTest.java
package com.abin.lee.easymock.servlets;
import org.easymock.EasyMock;
import org.easymock.IMocksControl;
import org.junit.Test;
import javax.servlet.http.HttpServletRequest;
import static junit.framework.TestCase.assertEquals;
import static org.junit.Assert.fail;
/**
* Created with IntelliJ IDEA.
* User: abin
* Date: 13-4-22
* Time: 下午5:08
* To change this template use File | Settings | File Templates.
*/
public class EasyMockServletFailedTest {
@Test
public void testEasyMockServletFailed(){
HttpServletRequest request=EasyMock.createMock(HttpServletRequest.class);
EasyMock.expect(request.getParameter("userName")).andReturn("abin");
EasyMock.expect(request.getParameter("passWord")).andReturn("varyall").times(1);
EasyMock.replay(request);
EasyMockServlet easyMockServlet=new EasyMockServlet();
try {
easyMockServlet.doPost(request,null);
fail("Not caught exception!");
}catch(Exception e){
assertEquals("Login failed.", e.getMessage());
e.printStackTrace();
}
EasyMock.verify(request);
}
}
//EasyMockServletSuccessTest
package com.abin.lee.easymock.servlets;
import org.easymock.EasyMock;
import org.easymock.IMocksControl;
import org.junit.Test;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* Created with IntelliJ IDEA.
* User: abin
* Date: 13-4-22
* Time: 下午7:49
* To change this template use File | Settings | File Templates.
*/
public class EasyMockServletSuccessTest {
@Test
public void testEasyMockServletSuccess() throws ServletException, IOException {
IMocksControl control= EasyMock.createControl();
HttpServletRequest request=control.createMock(HttpServletRequest.class);
// HttpServletResponse response=control.createMock(HttpServletResponse.class);
final ServletContext servletContext=control.createMock(ServletContext.class);
RequestDispatcher requestDispatcher=control.createMock(RequestDispatcher.class);
EasyMock.expect(request.getParameter("userName")).andReturn("abin").once();
EasyMock.expect(request.getParameter("passWord")).andReturn("varyall").once();
EasyMock.expect(servletContext.getNamedDispatcher("dispatcher")).andReturn(requestDispatcher).times(1);
requestDispatcher.forward(request,null);
EasyMock.expectLastCall();
control.replay();
EasyMockServlet easyMockServlet=new EasyMockServlet(){
public ServletContext getServletContext(){
return servletContext;
}
} ;
easyMockServlet.doPost(request,null);
control.verify();
}
}
]]>研究q程
Mock技?nbsp;
代码
{
return GetNextFiredDate(now, trigger.TriggerType, trigger.TriggerExpression, triggeredtimes);
}
{
// some method here
}代码
using System.Collections.Generic;
using System.Text;
using System.Runtime.Remoting.Proxies;
using System.Runtime.Remoting.Messaging;
namespace Pixysoft.Framework.TestDrivens
{
public class Mock<TInterface> : RealProxy
{
public Mock()
: base(typeof(TInterface))
{
}
public TInterface Value
{
get
{
return (TInterface)this.GetTransparentProxy();
}
}
public override IMessage Invoke(IMessage msg)
{
IMethodCallMessage methodCall = msg as IMethodCallMessage;
//我返回int = 1
return new ReturnMessage(1, null, 0, null, methodCall);
}
}
public interface IMock
{
int Devide(int a, int b);
}
public class testrealproxy //试代码在这里!Q!
{
public void test()
{
IMock mock = new Mock<IMock>().Value;
Console.WriteLine(mock.Devide(1, 2));
//输出 = 1
}
}
}后记
]]>
]]>
<dependency>
<groupId>org.easymock</groupId>
<artifactId>easymock</artifactId>
<version>3.0</version>
<scope>test</scope>
</dependency>
EasyMock.replay(pingJiaDao);
double rate = storeService.getStoreGoodRate(storeId);
Q?Q参数匹配器
Q?Q重|mock对象
Q?Q模拟异常抛?br style="line-height: normal !important; word-break: break-all" /> Q?Q设|调用次?/p>
]]>
]]>
package com.abin.lee.mock;
public class User {
private int id;
private String userName;
private String passWord;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getPassWord() {
return passWord;
}
public void setPassWord(String passWord) {
this.passWord = passWord;
}
}
package com.abin.lee.mock;
public interface UserDao {
User query(String id);
}
package com.abin.lee.mock;
public class UserDaoImpl implements UserDao{
public User query(String id) {
User user=null;
if(id.equals("1")){
user=new User();
user.setId(1);
user.setUserName("abin1");
user.setPassWord("varyall1");
}
if(id.equals("2")){
user=new User();
user.setId(2);
user.setUserName("abin2");
user.setPassWord("varyall2");
}
return user;
}
}
package com.abin.lee.mock;
public interface UserService {
User query(String id);
}
package com.abin.lee.mock;
public class UserServiceImpl implements UserService{
private UserDao userDao;
public User query(String id){
return this.userDao.query(id);
}
public UserDao getUserDao() {
return userDao;
}
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
}
package com.abin.lee.mock;
import org.easymock.EasyMock;
import org.junit.Assert;
import org.junit.Test;
public class UserMock {
@Test
public void test(){
User expectedUser=new User();
expectedUser.setId(1);
expectedUser.setUserName("abin1");
expectedUser.setPassWord("varyall1");
UserDao userDao=EasyMock.createMock(UserDao.class);
EasyMock.expect(userDao.query("1")).andReturn(expectedUser);
EasyMock.replay(userDao);
UserServiceImpl service=new UserServiceImpl();
service.setUserDao(userDao);
User user=service.query("1");
Assert.assertNotNull(user);
Assert.assertEquals(1, user.getId());
Assert.assertEquals("abin1", user.getUserName());
Assert.assertEquals("varyall1", user.getPassWord());
EasyMock.verify(userDao);
}
}
试Ҏ2Q?br />
package com.abin.lee.mock;
import org.easymock.EasyMock;
import org.easymock.IMocksControl;
import org.junit.Assert;
import org.junit.Test;
public class UsersMock {
@Test
public void test(){
User expectedUser=new User();
expectedUser.setId(2);
expectedUser.setUserName("abin2");
expectedUser.setPassWord("varyall2");
IMocksControl mock=EasyMock.createNiceControl();
UserDao userDao=mock.createMock(UserDao.class);
EasyMock.expect(userDao.query("2")).andReturn(expectedUser);
mock.replay();
UserServiceImpl service=new UserServiceImpl();
service.setUserDao(userDao);
User user=service.query("2");
Assert.assertNotNull(user);
Assert.assertEquals(2, user.getId());
Assert.assertEquals("abin2", user.getUserName());
Assert.assertEquals("varyall2", user.getPassWord());
mock.verify();
mock.resetToNice();
}
}
import java.io.IOException;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class ProcessServlet extends HttpServlet{
public void init() throws ServletException {
super.init();
}
@SuppressWarnings("rawtypes")
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String username=request.getParameter("username");
String password=request.getParameter("password");
System.out.println("username="+username);
System.out.println("password="+password);
ServletContext context = getServletContext();
RequestDispatcher dispatcher = context.getNamedDispatcher("dispatcher");
dispatcher.forward(request, response);
// ServletOutputStream out=response.getOutputStream();
// BufferedWriter writer=new BufferedWriter(new OutputStreamWriter(out));
// writer.write("success");
// writer.flush();
// writer.close();
}
public void destroy() {
super.destroy();
}
}
package com.abin.lee.servlet.process;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import junit.framework.TestCase;
import org.easymock.EasyMock;
import org.junit.Before;
import org.junit.Test;
public class ServletMock extends TestCase{
private static HttpServletRequest request =null;
private static HttpServletResponse response=null;
private static ServletContext context=null;
private static RequestDispatcher dispatcher=null;
private static ProcessServlet servlet=null;
@Before
public void setUp(){
request =EasyMock.createMock(HttpServletRequest.class);
response=EasyMock.createMock(HttpServletResponse.class);
context=EasyMock.createMock(ServletContext.class);
dispatcher=EasyMock.createMock(RequestDispatcher.class);
servlet=new ProcessServlet(){
private static final long serialVersionUID = 7534303474286669635L;
public ServletContext getServletContext(){
return context;
}
};
}
@Test
public void test() throws ServletException, IOException{
EasyMock.expect(request.getParameter("username")).andReturn("abin").times(20000);
EasyMock.expect(request.getParameter("password")).andReturn("varyall").times(20000);
EasyMock.expectLastCall();
EasyMock.replay(request);
EasyMock.replay(response);
servlet.doPost(request, response);
EasyMock.verify(response);
dispatcher.forward(request, response);
StringWriter sw=new StringWriter();
PrintWriter writer=new PrintWriter(sw, true);
response.getOutputStream();
// response.set
// String line="";
// String result="";
// while((line=writer.)){
//
// }
// BufferedReader reader=new BufferedReader(new InputStreamReader());
}
}