少年阿宾

那些青春的岁月

  BlogJava :: 首页 :: 联系 :: 聚合  :: 管理
  500 Posts :: 0 Stories :: 135 Comments :: 0 Trackbacks

#

//UserDao.java

package com.abin.lee.jmock;

/**
 * Created with IntelliJ IDEA.
 * User: abin
 * Date: 13-4-23
 * Time: 上午11:07
 * To change this template use File | Settings | File Templates.
 */
public interface UserDao {
    public String getMessage(String message);
}





//UserService.java

package com.abin.lee.jmock;

/**
 * Created with IntelliJ IDEA.
 * User: abin
 * Date: 13-4-23
 * Time: 上午11:06
 * To change this template use File | Settings | File Templates.
 */
public class UserService {
   private UserDao userDao;

   public String findMessage(String message){
       return this.userDao.getMessage(message);
   }

    public void setUserDao(UserDao userDao){
        this.userDao=userDao;
    }
}





//UserManageTest.java

package com.abin.lee.jmock;

import junit.framework.TestCase;
import org.jmock.Expectations;
import org.jmock.Mockery;
import org.junit.Test;

/**
 * Created with IntelliJ IDEA.
 * User: abin
 * Date: 13-4-23
 * Time: 上午11:08
 * To change this template use File | Settings | File Templates.
 */
public class UserManageTest {
    @Test
    public void testUserManage(){
        // 建立一个test上下文对象。
        Mockery mockery=new Mockery();
        // 生成一个mock对象
        final UserDao userDao=mockery.mock(UserDao.class);
        // 设置期望。
        mockery.checking(new Expectations(){
                {
                    // 当参数为"abin"的时候,userDao对象的getMessage方法被调用一次,并且返回西安。
                    oneOf(userDao).getMessage("abin");
                    will(returnValue("abin"));
                }
        });
        UserService userService=new UserService();
        userService.setUserDao(userDao);
        String message=userService.findMessage("abin");
        System.out.println("message="+message);
        TestCase.assertEquals("abin",message);

    }
}

posted @ 2013-04-23 11:29 abin 阅读(575) | 评论 (0)编辑 收藏

手头的项目越来越大,很多以前不会出现的问题开始浮现。

 

比如:我修改了一个基础的类库,却意外的影响了九重天外的客户项目,直接导致一个功能无法实现。我郁闷啊!!!

 

因此开始要有组织、有预谋、有计划的对项目过程进行测试驱动了。最终目标是,我修改了底层某个dll的某个方法,测试框架能够自动帮我找出来所有收到影响的类,全部执行一次回归测试,并发送一份漂亮的报告到我手里。

 

这个目标估计1、2个星期才能实现,不过现在先放出一个非常漂亮的MOCK核心代码。 

 

研究过程

在不断收集各种资料过程中,学习了很多,例如以下关键字,有兴趣的兄弟可以自己搜索一下:

 

testdriven.net, nunit,  typemock, cruiseControl.net, Confluence, JIRE, NUnitForms, WatiN, MBUnit, CSUnit, NBehave, Gallio

ranorex,  dynamicProxy...

 

估计各位有时间看看上面的简介,就能够掌握现在测试驱动的大致发展。

 

  1. nunit的核心代码非常容易理解,大伙自己下载看看就行了。
  2. testdriven.net 的前身是:NUnitAddin, 如果要研究如何testdriven集成到vs,看看不错。
  3. WatiN的核心代码虽然我没有看,不过猜也能猜到,就是调用了IE的核,然后搜索上面的html标签操作。
  4. typeMock有点混蛋,源码混淆了,无法拿到核心技术,不过从介绍来看是源自了castle.DynamicProxy,那么各位可以参观一下一篇非常垃圾的文章,但是起码让我入门了: http://www.cublog.cn/u/23701/showart_1414436.html
  5. 最后,我来到了Moq,开始因为听说是.net3.5,就没有看源码,不过刚才研究了一下顿时非常兴奋。起码Moq能让我解决了50%的工作。

 

接下来就说下Mock技术和测试驱动中的作用。

 

Mock技术 

我最不喜欢老外造名词,所以会用自己的体会去介绍。

mock的本质就是模拟目标对象的一个假对象。 

这个性质在测试驱动中非常有用,例如我的代码是:

代码
        public DateTime GetNextFiredDate(DateTime now, IOrmScheduleTrigger trigger, int triggeredtimes)
        {
            
return GetNextFiredDate(now, trigger.TriggerType, trigger.TriggerExpression, triggeredtimes);
        }

 

 

 现在要测试这个代码,就需要传入一个IOrmScheduleTrrigger的接口对象。但是不幸的是,这个对象是个ORM,要启动这个对象,就涉及到了数据库。。。。

 

老大,我只是想测试一下一辆宝马的玻璃是否坚硬,不需要启动我的宝马加速到120km,然后再用手去翘翘那块玻璃吧。

 

所以,我希望能够有个模拟对象,继承了这个接口, 同时提供了我期望的返回值,让这个方法能够顺利执行。

 

传统的傻逼方法,就是自己写一个类,继承了这个接口,然后才传入进去。例如:

 

public class OrmScheduleTriggerTestObject : IOrmScheduleTrigger
{
// some method here
}

 

 

这样不就更加的傻逼了?我为了测一块玻璃,还亲自造了另外一台简易的宝马出来? 于是我开始翻阅各种文献,甚至考虑使用动态代理(DynamicProxy)。动态代理的核心思想就是在代码运行中写IL生成一个继承类。这个技术很有用,但是现在我还用不上(就像martin fowler说的,typemock就等于把核武器交给了一个4岁小孩)。

 

于是我继续寻找,终于翻开了Moq的源码,找到了答案。

 

先看看以下一段代码,是我摘自Moq源码的核心部分,稍加改造了:

 

复制代码
代码
using System;
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(1null0null, methodCall);
        }
    }

    
public interface IMock
    {
        
int Devide(int a, int b);
    }

    
public class testrealproxy //测试代码在这里!!!
    {
        
public void test()
        {
            IMock mock 
= new Mock<IMock>().Value;

            Console.WriteLine(mock.Devide(
12));

            
//输出 = 1
        }
    }
}
复制代码

 

 

这段代码就是Moq的核心思想。

 

大概意思是:我希望调用接口IMock的方法Devide,但是我压根不想写这个接口的实现。

 

那么我先写一个通用的模拟对象Mock<TInterface>,继承了RealProxy。

 

然后通过调用Value就可以返回需要的接口对象。而这个对象就是 return (TInterface)this.GetTransparentProxy();是个透明代理。

 

最后当我调用了 int Devide(int a, int b); 方法的时候,等于调用了public override IMessage Invoke(IMessage msg)方法(有点点的AOP感觉)。

 

后记

 

上文就是Moq的核心思想了。非常的精彩!估计有了思路,各位就可以制造自己的原子弹了。

 

这里插句题外话,很多人抨击重复造轮子。我就奇怪了。如果我造一个轮子花费的时间和学习用一个轮子的时间差不远,为什么不造一个?

而且,用别人的轮子,经常出现的情况是:很多轮子不知道挑哪个。一旦挑上了,项目进展到一般才发现不适合、有bug,于是又重头挑另外的轮子。

 

这个经历是真实的。当年读大学,我的室友就是典型的挑轮子,他懂得很多框架(java),webwork,hibernate, spring。和人砍起来朗朗上口,但是需要深入做项目了,出现问题基本上不知所措,不是翻文献,就是问师兄,最后整个项目组从来就没有一个成品。

 

我自从学电脑依赖,从来就没有用过别人的轮子,即使是hibernate,我的确也没有用过,不过他的核心文档倒是看过,对比之下,和oracle的toplink相比简直就是小孩。

 

比我牛逼的兄弟大有人在,希望各位牛人不要浪费自己的时间去挑别人的轮子,直接自己造一个算了。 

 

最后说说接下来的工作。

 

基于接口的测试驱动完成了,剩下的就是面对sealed class 等顽固分子了, 必然需要动用非常规武器,DynamicProxy。下回再见。

 

 

分享到:
posted @ 2013-04-20 15:29 abin 阅读(483) | 评论 (0)编辑 收藏

1 现有的单元测试框架
       单元测试是保证程序正确性的一种有效的测试手段,对于不同的开发语言,通常都能找到相应的单元框架。



       借助于这些单测框架的帮助,能够使得我们编写单元测试用例的过程变得便捷而优雅。框架帮我们提供了case的管理,执行,断言集,运行参数,全局事件工作,所有的这些使得我们只需关注:于对于特定的输入,被测对象的返回是否正常。
       那么,这些xUnit系列的单元测试框架是如何做到这些的了?分析这些框架,发现所有的单元测试框架都是基于以下的一种体系结构设计的。

 
       如上图所示,单测框架中通常包括TestRunner, Test, TestResult, TestCase, TestSuite, TestFixture六个组件。
TestRuner:负责驱动单元测试用例的执行,汇报测试执行的结果,从而简化测试
TestFixture:以测试套件的形式提供setUp()和tearDown()方法,保证两个test case之间的执行是相互独立,互不影响的。
TestResult:这个组件用于收集每个test case的执行结果
Test:作为TestSuite和TestCase的父类暴露run()方法为TestRunner调用
TestCase:暴露给用户的一个类,用户通过继承TestCase,编写自己的测试用例逻辑
TestSuite:提供suite功能管理testCase
       正因为相似的体系结构,所以大多数单元测试框架都提供了类似的功能和使用方法。那么在单测中引入单元测试框架会带来什么好处,在现有单元测试框架下还会存在什么样不能解决的问题呢?
2 单元测试框架的优点与一些问题
       在单元测试中引入单测框架使得编写单测用例时,不需要再关注于如何驱动case的执行,如何收集结果,如何管理case集,只需要关注于如何写好单个测试用例即可;同时,在一些测试框架中通过提供丰富的断言集,公用方法,以及运行参数使得编写单个testcase的过程得到了最大的简化。
       那这其中会存在什么样的疑问了?
       我在单元测试框架中写一个TestCase,与我单独写一个cpp文件在main()方法里写测试代码有什么本质却别吗?用了单元测试框架,并没有解决我在对复杂系统做单测时遇到的问题。
       没错,对于单个case这两者从本质上说是没有区别的。单元测试框架本身并没有告诉你如何去写TestCase,在这一点上他是没有提供任何帮助的。所以对于一些复杂的场景,只用单元测试框架是有点多少显得无能为力的。
       使用单元测试框架往往适用于以下场景的测试:单个函数,一个class,或者几个功能相关class的测试,对于纯函数测试,接口级别的测试尤其适用,如房贷计算器公式的测试。
       但是,对于一些复杂场景:
 被测对象依赖复杂,甚至无法简单new出这个对象
 对于一些failure场景的测试
 被测对象中涉及多线程合作
 被测对象通过消息与外界交互的场景
 …
       单纯依赖单测框架是无法实现单元测试的,而从某种意义上来说,这些场景反而是测试中的重点。
       以分布式系统的测试为例,class 与 function级别的单元测试对整个系统的帮助不大,当然,这种单元测试对单个程序的质量有帮助;分布式系统测试的要点是测试进程间的交互:一个进程收到客户请求,该如何处理,然后转发给其他进程;收到响应之后,又修改并应答客户;同时分布式系统测试中通常更关注一些异常路径的测试,这些场景才是测试中的重点,也是难点所在。
       Mock方法的引入通常能帮助我们解决以上场景中遇到的难题。
3 Mock的引入带来了什么
       在维基百科上这样描述Mock:In object-oriented programming, mock objects are simulated objects that mimic the behavior of real objects in controlled ways. A computer programmer typically creates a mock object to test the behavior of some other object, in much the same way that a car designer uses a crash test dummy to simulate the dynamic behavior. of a human in vehicle impacts.
       Mock通常是指,在测试一个对象A时,我们构造一些假的对象来模拟与A之间的交互,而这些Mock对象的行为是我们事先设定且符合预期。通过这些Mock对象来测试A在正常逻辑,异常逻辑或压力情况下工作是否正常。
       引入Mock最大的优势在于:Mock的行为固定,它确保当你访问该Mock的某个方法时总是能够获得一个没有任何逻辑的直接就返回的预期结果。
       Mock Object的使用通常会带来以下一些好处:
 隔绝其他模块出错引起本模块的测试错误。
 隔绝其他模块的开发状态,只要定义好接口,不用管他们开发有没有完成。
 一些速度较慢的操作,可以用Mock Object代替,快速返回。
       对于分布式系统的测试,使用Mock Object会有另外两项很重要的收益:
 通过Mock Object可以将一些分布式测试转化为本地的测试
 将Mock用于压力测试,可以解决测试集群无法模拟线上集群大规模下的压力
4 Mock的应用场景
       在使用Mock的过程中,发现Mock是有一些通用性的,对于一些应用场景,是非常适合使用Mock的:
 真实对象具有不可确定的行为(产生不可预测的结果,如股票的行情)
 真实对象很难被创建(比如具体的web容器)
 真实对象的某些行为很难触发(比如网络错误)
 真实情况令程序的运行速度很慢
 真实对象有用户界面
 测试需要询问真实对象它是如何被调用的(比如测试可能需要验证某个回调函数是否被调用了)
 真实对象实际上并不存在(当需要和其他开发小组,或者新的硬件系统打交道的时候,这是一个普遍的问题)
       当然,也有一些不得不Mock的场景:
 一些比较难构造的Object:这类Object通常有很多依赖,在单元测试中构造出这样类通常花费的成本太大。
 执行操作的时间较长Object:有一些Object的操作费时,而被测对象依赖于这一个操作的执行结果,例如大文件写操作,数据的更新等等,出于测试的需求,通常将这类操作进行Mock。
 异常逻辑:一些异常的逻辑往往在正常测试中是很难触发的,通过Mock可以人为的控制触发异常逻辑。
        在一些压力测试的场景下,也不得不使用Mock,例如在分布式系统测试中,通常需要测试一些单点(如namenode,jobtracker)在压力场景下的工作是否正常。而通常测试集群在正常逻辑下无法提供足够的压力(主要原因是受限于机器数量),这时候就需要应用Mock去满足。
        在这些场景下,我们应该如何去做Mock的工作了,一些现有的Mock工具可以帮助我们进行Mock工作。
5 Mock工具的介绍
       手动的构造 Mock 对象通常带来额外的编码量,而且这些为创建 Mock 对象而编写的代码很有可能引入错误。目前,有许多开源项目对动态构建 Mock 对象提供了支持,这些项目能够根据现有的接口或类动态生成,这样不仅能避免额外的编码工作,同时也降低了引入错误的可能。
C++:   GoogleMock   http://code.google.com/p/googlemock/

Java:   EasyMock   http://easymock.org/

       通常Mock工具通过简单的方法对于给定的接口生成 Mock 对象的类库。它提供对接口的模拟,能够通过录制、回放、检查三步来完成大体的测试过程,可以验证方法的调用种类、次数、顺序,可以令 Mock 对象返回指定的值或抛出指定异常。通过这些Mock工具我们可以方便的构造 Mock 对象从而使单元测试顺利进行,能够应用于更加复杂的测试场景。
       以EasyMock为例,通过 EasyMock,我们可以为指定的接口动态的创建 Mock 对象,并利用 Mock 对象来模拟协同模块,从而使单元测试顺利进行。这个过程大致可以划分为以下几个步骤:
 使用 EasyMock 生成 Mock 对象
 设定 Mock 对象的预期行为和输出 
 将 Mock 对象切换到 Replay 状态
 调用 Mock 对象方法进行单元测试
 对 Mock 对象的行为进行验证
EasyMock的使用和原理:  http://www.ibm.com/developerworks/cn/opensource/os-cn-easymock/

       EasyMock 后台处理的主要原理是利用 java.lang.reflect.Proxy 为指定的接口创建一个动态代理,这个动态代理,就是我们在编码中用到的 Mock 对象。EasyMock 还为这个动态代理提供了一个 InvocationHandler 接口的实现,这个实现类的主要功能就是将动态代理的预期行为记录在某个映射表中和在实际调用时从这个映射表中取出预期输出。
       借助类似于EasyMock这样工具,大大降低了编写Mock对象的成本,通常来说Mock工具依赖于单元测试框架,为用户编写TestCase提供便利,但是本身依赖于单元测试框架去驱动,管理case,以及收集测试结果。例如EasyMock依赖于JUint,GoogleMock依赖于Gtest。
       那么有了单元测试框架和相应的Mock工具就万事俱备了,还有什么样的问题?正如单元测试框架没有告诉你如何写TestCase一样,Mock工具也没有告诉你如何去选择Mock的点。
6 如何选择恰当的mock点
       对于Mock这里存在两个误区,1.是Mock的对象越多越好;2.Mock会引入巨大的工作量,通常得不偿失。这都是源于不恰当的Mock点的选取。
       这里说的如何选择恰当的mock点,是说对于一个被测对象,我们应当在外围选择恰当的mock对象,以及需要mock的接口。因为对于任意一个对象,任意一段代码逻辑我们都是有办法进行Mock的,而Mock点选择直接决定了我们Mock的工作量以及测试效果。从另外一种意义上来说,不恰当Mock选择反而会对我们的测试产生误导,从而在后期的集成和系统测试中引入更多的问题。
       在mock点的选择过程中,以下的一些点会是一些不错的选择
 网络交互:如果两个被测模块之间是通过网络进行交互的,那么对于网络交互进行Mock通常是比较合适的,如RPC
 外部资源:比如文件系统、数据源,如果被测对象对此类外部资源依赖性非常强,而其行为的不可预测性很可能导致测试的随机失败,此类的外部资源也适合进行Mock。
 UI:因为UI很多时候都是用户行为触发事件,系统本身只是对这些触发事件进行相应,对这类UI做Mock,往往能够实现很好的收益,很多基于关键字驱动的框架都是基于UI进行Mock的
 第三方API:当接口属于使用者,通过Mock该接口来确定测试使用者与接口的交互。
       当然如何做Mock一定是与被系统的特性精密关联的,一些强制性的约束和规范是不合适的。这里介绍几个做的比较好的mock的例子。
       1. 杀毒软件更新部署模块的Mock
       这个例子源于一款杀毒产品的更新部署模块的测试。对于一个杀毒软件客户端而言,需要通过更新检查模块与病毒库Server进行交互,如果发现病毒库有更新则触发病毒库部署模块的最新病毒库的数据请求和部署工作,要求部署完成后杀毒软件客户端能够正常工作。
 

        对于这一场景的测试,当时受限于这样一个条件,通常的病毒库server通常最多一天只更新一次病毒库,也就是说如果使用真实的病毒库server,那么针对更新部署模块的测试一天只能被触发一次。这是测试中所不能容忍的,通过对病毒库server进行mock可以解决这个问题。
       对于这个场景可以采取这样一种Mock方式:用一个本地文件夹来模拟病毒库server,选择更新部署模块与病毒库server之间交互的两个函数checkVersion(),reqData()函数进行Mock。
       checkVersion()工作原先的工作是检查病毒库Server的版本号,以决定是否触发更新,将其行为Mock为检查一个本地文件夹中病毒库的版本号;reqData()原有的行为是从病毒库Server拖取病毒库文件,将其Mock为从本地文件夹中拖取病毒库文件。通过这种方式我们用一个本地文件夹Mock病毒库Server的行为,其带来的产出是:我们可以随意的触发病毒库更新操作以及各种异常。通过这种方式发现了一个在更新部署过程中,病毒库Server的病毒库版本发生改变造成出错的严重bug,这个是在原有一天才触发一次更新操作的情况下永远也无法发现的。
       2. 分布式系统中对NameNode模块的测试
 

       在测试NameNode模块的过程中存在这样一个问题,在正常逻辑无压力条件下NameNode模块都是工作正常的。但是线上集群在大压力的情况下,是有可能触发NameNode的问题的。但是原有的测试方法下,我们是无法对NameNode模拟大压力的场景的(因为NameNode的压力主要来源于DateNode数量,而我们测试集群是远远无法达到线上几千台机器的规模的),而NameNode单点的性能瓶颈问题恰恰是测试的重点,真实的DataNode是无法满足测试需求的,我们必须对DataNode进行Mock。
 

       如何对DateNode进行Mock了,最直观的想法是选择NameNode与DataNode之间的交互接口进行Mock,也就是他们之间的RPC交互,但是由于NameNode与DataNode之间的交互信息种类很多,所以其实这并不是一种很好的选择。
       换个角度来想,NameNode之上的压力是源于对HDFS的读写操作造成的NameNode上元数据的维护,也就是说,对于NameNode而言,其实他并不关心数据到底写到哪里去了,只关心数据是否读写成功。如果是这种场景Mock就可以变的简单了,我们可以直接将DataNode上对块的操作进行mock,比如,对一次写请求,DataNode并不触发真实的写操作,而直接返回成功。通过这种方式,DataNode去除了执行功能,只保留了消息交互功能,间接的实现了我们的测试需求,且工作量比之第一种方案小很多。
       3. 开源社区提供的MRUnit测试框架
       在原有框架下,对于MapReduce程序的测试通常是无法在本地验证的,更不用说对MapReduce程序进行单测了。而MRUnit通过一个简单而优雅的Mock,却实现了一个基于MapReduce程序的单测框架。

基于MRUINT框架可以将单测写成如下形式:

 

       在这个框架中定义了MapDriver,ReducerDriver,MapReduceDriver三个有点类似容器的driver,通过driver来驱动map,reduce或者整个mapreduce过程的执行。
       如上例,在driver中设定mapper为IdentityMapper,通过withInput方法设定输入数据,通过withOutput方法设定预期结果,通过runTest方法来触发执行并进行结果检测
       他的实现原理是将outputCollector做Mock,outputCollectort中的emit方法实现的逻辑是将数据写到文件系统中,Mock后是通过另外一个进程去收集数据并保存在内存中,从而实现最终结果的可检验(在自己的数据结构中比对结果)。
       实现的原理很简单,这样做mock就会精巧,只选择最底层的一些简单却又依赖广泛的点(依赖广泛指模块间的数据流通常都走这样的点过)做mock,这样通常效果很好且简单
       当然这个例子中也有一些缺陷:1.因为在outputcollector层做mock的数据截取,使得无法过partition的分桶逻辑;2.这个框架是写内存的,无法最终改成压力性能测试工具。

7 附录
1. EasyMock示例:
 

posted @ 2013-04-20 15:21 abin 阅读(405) | 评论 (0)编辑 收藏

1、什么情况下会使用mock技术

  (1)需要将当前被测单元和其依赖模块独立开来,构造一个独立的测试环境,不关注被测单元的依赖对象,只关注被测单元的功能逻辑

  ----------比如被测代码中需要依赖第三方接口返回值进行逻辑处理,可能因为网络或者其他环境因素,调用第三方经常会中断或者失败,无法对被测单元进行测试,这个时候就可以使用mock技术来将被测单元和依赖模块独立开来,使得测试可以进行下去。

  (2)被测单元依赖的模块尚未开发完成,而被测单元需要依赖模块的返回值进行后续处理

  ----------比如service层的代码中,包含对Dao层的调用,但是,DAO层代码尚未实现

  (3)被测单元依赖的对象较难模拟或者构造比较复杂

  ----------比如,支付宝支付的异常条件有很多,但是模拟这种异常条件很复杂或者无法模拟,比如,查询聚划算的订单结果,无法在测试环境进行模拟

  2、Mock技术分类

  (1)手动构造mock对象

  ---------------比如,可以自己写某个接口方法的实现,根据需要编写返回值,测试代码中使用该实现类对象

  缺点:会增加代码量,在写mock对象代码时,有可能引入错误

  (2)使用开源代码提供的构造mock方法

  --------------比如easyMock,提供了对接口类的模拟,能够通过录制、回放、检查三步来完成大体的测试过程,可以验证方法的调用种类、次数、顺序,可以令Mock对象返回指定的值或抛出指定异常

  3、EasyMock使用

  (1)引入easyMock

  ------------在maven工程中,通过pom配置依赖关系

<dependency>
    <groupId>org.easymock</groupId>
    <artifactId>easymock</artifactId>
    <version>3.0</version>
    <scope>test</scope>
</dependency>

  ------------在普通java工程中,通过添加外部包的方式

  (2)使用easyMock过程

  1)使用EasyMock生成Mock对象;
  pingJiaDao = mockControl.createMock(IPingJiaDao.class);

  2)设定Mock对象的预期行为和输出;
  EasyMock.expect(pingJiaDao.getGoodPingJiaRate(storeId)).andReturn(0.11);

  3)将Mock对象切换到Replay状态;
  EasyMock.replay(pingJiaDao);

  4)调用Mock对象方法进行单元测试
  storeService.setStoredao(pingJiaDao);
  double rate = storeService.getStoreGoodRate(storeId);

  5)对Mock对象的行为进行验证。
  EasyMock.verify(pingJiaDao);

  4、其他easyMock功能

  (1)特殊的mock对象:niceMock
  (2)参数匹配器
  (3)重置mock对象
  (4)模拟异常抛出
  (5)设置调用次数

posted @ 2013-04-20 15:19 abin 阅读(407) | 评论 (0)编辑 收藏

  /** 
     * HTTP DELETE方法进行删除操作 
     * @param url 
     * @param map 
     * @return 
     * @throws ClientProtocolException 
     * @throws IOException 
     */  
    public static String remoteDelete(String url, Map<String, String> map) throws ClientProtocolException, IOException{  
        url = JETSUM_PLATFORM_SERVER+url;  
        HttpClient httpclient = new DefaultHttpClient();  
        HttpDelete httpdelete= new HttpDelete();  
        List<NameValuePair> formparams = setHttpParams(map);  
        String param = URLEncodedUtils.format(formparams, "UTF-8");  
        httpdelete.setURI(URI.create(url + "?" + param));  
        HttpResponse response = httpclient.execute(httpdelete);  
        String httpEntityContent = GetHttpEntityContent(response);  
        httpdelete.abort();  
        return httpEntityContent;     
    }       




   /** 
     * 设置请求参数 
     * @param map 
     * @return 
     */  
    private static List<NameValuePair> setHttpParams(Map<String, String> map) {  
        List<NameValuePair> formparams = new ArrayList<NameValuePair>();  
        Set<Map.Entry<String, String>> set = map.entrySet();  
        for (Map.Entry<String, String> entry : set) {  
            formparams.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));  
        }  
        return formparams;  
    }  



 /** 
     * 获得响应HTTP实体内容 
     * @param response 
     * @return 
     * @throws IOException 
     * @throws UnsupportedEncodingException 
     */  
    private static String GetHttpEntityContent(HttpResponse response)  
            throws IOException, UnsupportedEncodingException {  
        HttpEntity entity = response.getEntity();  
        if (entity != null) {  
            InputStream is = entity.getContent();  
            BufferedReader br = new BufferedReader(new InputStreamReader(is, "UTF-8"));  
            String line = br.readLine();  
            StringBuilder sb = new StringBuilder();  
            while (line != null) {  
                sb.append(line + "\n");  
                line = br.readLine();  
            }  
            return sb.toString();  
        }  
        return "";  
    }  
posted @ 2013-04-20 00:54 abin 阅读(799) | 评论 (0)编辑 收藏

怎么配置Tomcat支持HTTP Delete和Put 方法

如何配置Tomcat支持HTTP Delete和Put 方法
在tomcat web.xml文件中配置org.apache.catalina.servlets.DefaultServlet的
<init-param>     
<param-name>readonly</param-name>
<param-value>false</param-value>
</init-param>
readonly参数默认是true,即不允许delete和put操作,所以通过XMLHttpRequest对象的put或者delete方法访问就会报告http 403错误。为REST服务起见,应该设置该属性为false。

posted @ 2013-04-20 00:03 abin 阅读(1346) | 评论 (0)编辑 收藏

逻辑:

String url = "http://www.baidu.com";

//将要访问的url字符串放入HttpPost中

HttpPost httpPost = new HttpPost(url);

//请求头 放置一些修改http请求头和cookie

httpPost.setHeader("Accept", "application/json");

......

//如果是HttpPost或者HttpPut请求需要在请求里加参数,而HttpGet或者HttpDelete请求则可以直接拼接到url字符串后面

//向HttpPost中加入参数

List<NameValuePair> values = new ArrayList<NameValuePair>();

values.add(new NameValuePair("id", "1"));

values.add(new NameValuePair("name", "xiaohong"));

httpPost.setEntity(new UrlEncodeFormEntity(values, HTTP.UTF_8));  //进行转码

 

//实例HttpClient 并执行带有HttpPost的方法,返回HttpResponse 响应,再进行操作

HttpClient httpClient = new DefaultHttpClient();

HttpResponse httpResponse = httpClient.execute(httpPost);

int statusCode = httpResponse.getStatusLine().getStatusCode();  //返回状态码 ,用来进行识别或者判断访问结果

if(statusCode == 200){

  Instream in = httpResponse.getEntity().getContent();  //要处理该数据流是否为GZIP流

}

 

 

示例代码如下:

package cn.dratek.haoyingsheng.manager.client;

import cn.dratek.haoyingsheng.manager.util.ResourceUtil;
import net.dratek.browser.http.Cookie;
import net.dratek.browser.http.CookieManager;
import net.dratek.browser.http.URL;
import org.apache.http.*;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.protocol.HTTP;

import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.util.List;


public class HttpNetClient {
/**
* 所有get 请求底层调用方法
*
* @param url 请求url
* @return byte[] response data
*/
public static byte[] doGet(String url) {
InputStream in;
byte[] bre = null;
HttpResponse response;
CookieManager manager = CookieManager.getInstance();
if (url != null && url.length() != 0) {
URL myURL = URL.parseString(url);
Cookie[] cookies = manager.getCookies(myURL);
HttpGet httpGet = new HttpGet(url);
if (cookies != null && cookies.length > 0) {
StringBuilder sb = new StringBuilder();
for (Cookie ck : cookies) {
sb.append(ck.name).append('=').append(ck.value).append(";");
}
String sck = sb.toString();
if (sck.length() > 0) {
httpGet.setHeader("Cookie", sck);
}

}
httpGet.setHeader("Accept-Encoding", "gzip, deflate");
httpGet.setHeader("Accept-Language", "zh-CN");
httpGet.setHeader("Accept", "application/json, application/xml, text/html, text/*, image/*, */*");
try {
response = new DefaultHttpClient().execute(httpGet);
if (response != null) {
int statusCode = response.getStatusLine().getStatusCode();
if (statusCode == 200 || statusCode == 403) {
Header[] headers = response.getHeaders("Set-Cookie");
if (headers != null && headers.length > 0) {
for (Header header : headers) {
manager.setCookie(myURL, header.getValue());
}
}
in = response.getEntity().getContent();
if (in != null) {
bre = ResourceUtil.readStream(in);
}

}
}


} catch (IOException e) {
e.printStackTrace();
}
}


return bre;

}

/**
* 所有Post 请求底层调用方法
*
* @param url 请求url
* @param values 传递的参数
* @return byte[] 返回数据 or null
*/
public static byte[] doPost(String url, List<NameValuePair> values) {
System.out.println("url = " + url);
byte[] bytes = null;
HttpResponse response;
InputStream inputStream = null;
CookieManager manager = CookieManager.getInstance();
if (url != null && url.length() != 0) {
URL myurl = URL.parseString(url);
Cookie[] cookies = manager.getCookies(myurl);
HttpPost post = new HttpPost(url);
if (cookies != null && cookies.length > 0) {
StringBuilder sb = new StringBuilder();
for (Cookie ck : cookies) {
sb.append(ck.name).append('=').append(ck.value).append(";");
}
String sck = sb.toString();
if (sck.length() > 0) {
post.setHeader("Cookie", sck);
}

}
post.setHeader("Accept-Encoding", "gzip, deflate");
post.setHeader("Accept-Language", "zh-CN");
post.setHeader("Accept", "application/json, application/xml, text/html, text/*, image/*, */*");
DefaultHttpClient client = new DefaultHttpClient();
try {
if (values != null && values.size() > 0) {
post.setEntity(new UrlEncodedFormEntity(values, HTTP.UTF_8));
}
response = client.execute(post);
if (response != null) {
int statusCode = response.getStatusLine().getStatusCode();
if (statusCode == 200 || statusCode == 403) {
Header[] headers = response.getHeaders("Set-Cookie");
if (headers != null && headers.length > 0) {
for (Header header : headers) {
manager.setCookie(myurl, header.getValue());
}
}
inputStream = response.getEntity().getContent();
}
}

} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
if (inputStream != null) {
bytes = ResourceUtil.readStream(inputStream);
}
}
return bytes;
}

/**
* PUT基础请求
*
* @param url 请求地址
* @param values 提交参数
* @return byte[] 请求成功后的结果
*/
public static byte[] doPut(String url, List<NameValuePair> values) {
byte[] ret = null;

CookieManager manager = CookieManager.getInstance();
if (url != null && url.length() > 0) {
URL myUrl = URL.parseString(url);
StringBuilder sb = new StringBuilder();
Cookie[] cookies = manager.getCookies(myUrl);
if (cookies != null && cookies.length > 0) {
for (Cookie cookie : cookies) {
sb.append(cookie.name).append("=").append(cookie.value).append(";");
}

}
HttpPut request = new HttpPut(url);
String sck = sb.toString();
if (sck.length() > 0) {
request.setHeader("Cookie", sck);
}
request.setHeader("Accept-Encoding", "gzip, deflate");
request.setHeader("Accept-Language", "zh-CN");
request.setHeader("Accept", "application/json, application/xml, text/html, text/*, image/*, */*");

DefaultHttpClient client = new DefaultHttpClient();
if (values != null && values.size() > 0) {
try {
UrlEncodedFormEntity entity;
entity = new UrlEncodedFormEntity(values);
request.setEntity(entity);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
try {
HttpResponse response = client.execute(request);
if (response != null) {
StatusLine statusLine = response.getStatusLine();
int statusCode = statusLine.getStatusCode();
if (statusCode == 200 || statusCode == 403) {
Header[] headers = response.getHeaders("Set-Cookie");
if (headers != null && headers.length > 0) {
for (Header header : headers) {
manager.setCookie(myUrl, header.getValue());
}
}
HttpEntity entity = response.getEntity();
InputStream inputStream = entity.getContent();
if (inputStream != null) {
ret = ResourceUtil.readStream(inputStream);
inputStream.close();
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
return ret;
}

/**
* Delete基础请求
*
* @param url 请求地址
* @return 请求成功后的结果
*/
public static byte[] doDelete(String url) {

InputStream in;
byte[] bre = null;
HttpResponse response;
CookieManager manager = CookieManager.getInstance();
if (url != null && url.length() != 0) {
URL myurl = URL.parseString(url);
Cookie[] cookies = manager.getCookies(myurl);
HttpDelete delete = new HttpDelete(url);
if (cookies != null && cookies.length > 0) {
StringBuilder sb = new StringBuilder();
for (Cookie ck : cookies) {
sb.append(ck.name).append('=').append(ck.value).append(";");
}
String sck = sb.toString();
if (sck.length() > 0) {
delete.setHeader("Cookie", sck);
}

}
delete.setHeader("Accept-Encoding", "gzip, deflate");
delete.setHeader("Accept-Language", "zh-CN");
delete.setHeader("Accept", "application/json, application/xml, text/html, text/*, image/*, */*");
try {
response = new DefaultHttpClient().execute(delete);
if (response != null) {
int statusCode = response.getStatusLine().getStatusCode();
if (statusCode == 200 || statusCode == 403) {
Header[] headers = response.getHeaders("Set-Cookie");
if (headers != null && headers.length > 0) {
for (Header header : headers) {
manager.setCookie(myurl, header.getValue());
}
}
in = response.getEntity().getContent();
if (in != null) {
bre = ResourceUtil.readStream(in);
}

}
}


} catch (IOException e) {
e.printStackTrace();
}
}


return bre;
}

 

}










http://www.cnblogs.com/lianghui66/archive/2013/03/06/2946495.html

posted @ 2013-04-19 23:39 abin 阅读(1599) | 评论 (0)编辑 收藏

我是一个初学者. 
我建了一个classes表和一个students表,表示班级和学生,其中学生里面有一个外键关联到班级表. 
然后学生类里面建了一个classes的属性, 
用session取出学生后,如果关闭session的话,就无法读取到学生类里的classes值,没有关闭就能读取到. 
请问这个session会不会影响到其他用户的访问呢? 
就是说如果有两个用户并行操作数据库的话,一个用户的session不关闭影不影响另一个用户呢?







J2EE中最大的一个观念就是分层.. 

session是持久层的东东.不可窜到别的层.. 

你的这个问题其实就是延迟加载的问题. 

从理论的角度讲,最好是用一个就关一个.防止资源消耗. 
但由于hibernate中的延迟加载,所以出现了你的关闭session的话,就无法读取到学生类里的classes值问题. 
这个问题可以用Hibernate.initialize()来解决.也可就使用opensessionview的方式.spring中提供了这样的filter 
不知道这在使用中有没有使用spring.用了就比较方便,也不会出现你所说的 

引用
一个用户的session不关闭影不影响另一个用户

因为session由spring来管理,很安全,不会出现这个种并发问题. 

如果只是使用了Hibernate的话,那得注意了.你在servlet中直接创建session就可能出现并发问题,因为session不是线程安全的,而servlet是多线程的. 
这时可以使用ThreadLocal来解决这个问题. 

希望对你有所帮助!



posted @ 2013-04-19 21:28 abin 阅读(583) | 评论 (0)编辑 收藏

在一个应用程序中,如果DAO层使用Spring的hibernate模板,通过Spring来控制session的生命周期,则首选getCurrentSession 

使用Hibernate的大多数应用程序需要某种形式的“上下文相关的”session,特定的session在整个特定的上下文范围内始终有效。然而,对不同类型的应用程序而言,要给为什么是组成这种“上下文”下一个定义通常是困难的;不同的上下文对“当前”这个概念定义了不同的范围。在3.0版本之前,使用Hibernate的程序要么采用自行编写的基于ThreadLocal的上下文session(如下面代码),要么采用HibernateUtil这样的辅助类,要么采用第三方框架(比如Spring或Pico),它们提供了基于代理(proxy)或者基于拦截器(interception)的上下文相关session 

从3.0.1版本开始,Hibernate增加了SessionFactory.getCurrentSession()方法。一开始,它假定了采用JTA事务,JTA事务 定义了当前session的范围和上下文(scope and context)。Hibernate开发团队坚信,因为有好几个独立的JTA TransactionManager实现稳定可用,不论是否被部署到一个J2EE容器中,大多数(假若不是所有的)应用程序都应该采用JTA事务管理。 基于这一点,采用JTA的上下文相关session可以满足你一切需要。 

更好的是,从3.1开始,SessionFactory.getCurrentSession()的后台实现是可拔插的。因此,我们引入了新的扩展接口 (org.hibernate.context.CurrentSessionContext)和新的配置参数 (hibernate.current_session_context_class),以便对什么是“当前session”的范围和上下文(scope and context)的定义进行拔插。 

org.hibernate.context.JTASessionContext - 当前session根据JTA来跟踪和界定。这和以前的仅支持JTA的方法是完全一样的。 

org.hibernate.context.ThreadLocalSessionContext - 当前session通过当前执行的线程来跟踪和界定。 

这两种实现都提供了“每数据库事务对应一个session”的编程模型,也称作一请求一事务。即Hibernate的session的生命周期由数据库事务的生存来控制。假若你采用自行编写代码来管理事务(比如,在纯粹的J2SE,或者 JTA/UserTransaction/BMT),建议你使用Hibernate Transaction API来把底层事务实现从你的代码中隐藏掉。如果你在支持CMT的EJB容器中执行,事务边界是声明式定义的,你不需要在代码中进行任何事务或session管理操作。 

1、getCurrentSession()与openSession()的区别 
* 采用getCurrentSession()创建的session会绑定到当前线程中,而采用openSession() 
创建的session则不会 
* 采用getCurrentSession()创建的session在commit或rollback时会自动关闭,而采用openSession()创建的session必须手动关闭 

2、使用getCurrentSession()需要在hibernate.cfg.xml文件中加入如下配置: 
* 如果使用的是本地事务(jdbc事务) 
<property name="hibernate.current_session_context_class">thread</property> 
* 如果使用的是全局事务(jta事务) 
<property name="hibernate.current_session_context_class">jta</property> 

在SessionFactory启动的时候,Hibernate会根据配置创建相应的CurrentSessionContext,在 getCurrentSession()被调用的时候,实际被执行的方法是CurrentSessionContext.currentSession()。在currentSession()执行时,如果当前Session 为空,currentSession 会调用SessionFactory 的openSession。所以getCurrentSession() 对于Java EE 来说是更好的获取Session 的方法。 

sessionFactory.getCurrentSession()可以完成一系列的工作,当调用时,hibernate将session绑定到当前线程,事务结束后,hibernate将session从当前线程中释放,并且关闭session,当再次调用getCurrentSession()时,将得到一个新的session,并重新开始这一系列工作。 
这样调用方法如下: 
Session session = HibernateUtil.getSessionFactory().getCurrentSession();
session.beginTransaction();
Event theEvent = new Event();
theEvent.setTitle(title);
theEvent.setDate(theDate);
session.save(theEvent);
session.getTransaction().commit();



不需要close session了 

利于ThreadLocal模式管理Session 
   早在Java1.2推出之时,Java平台中就引入了一个新的支持:java.lang.ThreadLocal,给我们在编写多线程程序时提供了一种新的选择。ThreadLocal是什么呢?其实ThreadLocal并非是一个线程的本地实现版本,它并不是一个Thread,而是thread local variable(线程局部变量)。也许把它命名为ThreadLocalVar更加合适。线程局部变量(ThreadLocal)其实的功用非常简单,就是为每一个使用某变量的线程都提供一个该变量值的副本,是每一个线程都可以独立地改变自己的副本,而不会和其它线程的副本冲突。从线程的角度看,就好像每一个线程都完全拥有一个该变量。ThreadLocal是如何做到为每一个线程维护变量的副本的呢?其实实现的思路很简单,在ThreadLocal类中有一个Map,用于存储每一个线程的变量的副本。 


public class HibernateUtil {
    
    private static String CONFIG_FILE_LOCATION = "/hibernate.cfg.xml";
    
    //创建一个局部线程变量
    private static final ThreadLocal<Session> THREAD_LOCAL = new ThreadLocal<Session>();
    
    private static final Configuration cfg = new Configuration();
    
    private static SessionFactory sessionFactory;
    
    /*
     * 取得当前session对象
     
*/
    @SuppressWarnings("deprecation")
    public static Session currentSession() throws HibernateException {
        Session session = (Session)THREAD_LOCAL.get();
        if (session == null) {
            if (sessionFactory == null) {
                try {
                    cfg.configure(CONFIG_FILE_LOCATION);
                    sessionFactory = cfg.buildSessionFactory();
                } catch (Exception e) {
                    System.out.println("【ERROR】创建SessionFactory对象出错,原因是:");
                    e.printStackTrace();
                }
            }
            
            session = sessionFactory.openSession();
            THREAD_LOCAL.set(session);
        }
        return session;
    }

    public static void closeSession() throws HibernateException {
        Session session = (Session) THREAD_LOCAL.get();
        THREAD_LOCAL.set(null);
        if(session != null){
            session.close();
        }
    }
}











posted @ 2013-04-19 21:22 abin 阅读(1958) | 评论 (0)编辑 收藏

package com.abin.lee.hack;

import java.io.BufferedInputStream;

import org.apache.http.HttpHost;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.junit.Test;

public class HttpClientVisitTest {

 private static final String HttpUrl = "http://111.111.111.111/vote/ticket";
 private static final String HttpHost = "111.111.111.111";
 @Test
 public void testHttpClientVisit() {
  HttpClient httpClient = new DefaultHttpClient();
  HttpPost httpPost = new HttpPost(HttpUrl);
  httpPost.addHeader("Accept", "*/*");
  httpPost.addHeader("Accept-Language", "zh-cn");
  httpPost.addHeader("Referer", HttpUrl);
  httpPost.addHeader("Content-Type", "application/x-www-form-urlencoded");
  httpPost.addHeader("Cache-Control", "no-cache");
  httpPost.addHeader("Accept-Encoding", "gzip, deflate");
  httpPost.addHeader("User-Agent",
    "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0)");
  httpPost.addHeader("Host", HttpHost);
  httpPost.addHeader("Connection", "Keep-Alive");
//  HttpHost httpProxy = new HttpHost("222.222.222.222", 1443, "http");
//  httpClient.getParams().setParameter(AllClientPNames.DEFAULT_PROXY,httpProxy);
  StringBuffer params = new StringBuffer();
  String userName = "abin";
  String passWord= "varyall";
  String userAge= "12345";
  String homeTown= "china beijing";
  params.append("__userName ").append("=").append(userName )
    .append("&").append(passWord").append("=")
    .append(passWord).append("&").append("userAge")
    .append("=").append(userAge).append("&")
    .append("homeTown").append("=")
    .append(homeTown);
  HttpResponse httpResponse = null;
  String result = "";
  try {
   StringEntity reqEntity = new StringEntity(params.toString());
   httpPost.setEntity(reqEntity);
   HttpHost httpTarget = new HttpHost(HttpHost, 80, "http");
   httpResponse = httpClient.execute(httpTarget, httpPost);
   System.out.println("httpResponse=" + httpResponse.getStatusLine());

   BufferedInputStream buffer = new BufferedInputStream(httpResponse
     .getEntity().getContent());
   byte[] bytes = new byte[1024];
   int line = 0;
   StringBuilder builder = new StringBuilder();
   while ((line = buffer.read(bytes)) != -1) {
    builder.append(new String(bytes, 0, line));
   }
   result = new String(builder.toString());
  } catch (Exception e) {
   e.printStackTrace();
  } finally {
   if (httpPost.isAborted()) {
    httpPost.abort();
   }
   httpClient.getConnectionManager().shutdown();
  }
//   System.out.println("result="+result);

 }

}

posted @ 2013-04-18 20:57 abin 阅读(2287) | 评论 (0)编辑 收藏

仅列出标题
共50页: First 上一页 13 14 15 16 17 18 19 20 21 下一页 Last