在以上代码中,我们通过三个步骤完成模拟对象的操作实现。步骤1我们创建一个模拟对象,并与实际对象类型进行关联。步骤2我们将模拟实际对象的函数操作环境,这里我们设置函数名称、参数和期望返回值。步骤3中我们在模拟对象上完成我们的期望实现,如调用实际对象的方法应该返回指定的返回值等,最后我们就可以调用实际类型的对象啦。
我们对模拟对象的要求主要集中在方法的调用和返回值,所以许多操作都是围绕这方面进行的,下面我们看看JMock的这方面的实现机制,如何在模拟对象上设置期望以达到实际的效果。
Mock是我们要模拟的对象,它需要一个欲模拟的实际对象的类型,这里可以为接口或具体类(需要使用cglib)。模拟对象创建完毕后,我就要设置我们的期望,主要是针对函数的。在JMock中设置期望可以通过expects和stubs来实现,关于这两者的区别,大家可以参考一些文档,不过有一点就是Expection比stub有更多的约束条件(调用次数方面)。这里我们通过Mock的expects函数来实现期望, expects接受InvocationMatcher对象,主要是调用的匹配规则,如仅调用一次、调用多次或不调用等。expects的返回值为NameMatchBuilder,我们需要设定函数名称,我们通过method方法来指定函数名。method的返回值为ArgumentsMatchBuilder,这时我们需要设定该函数的参数约束信息,如果没有设置,表示匹配任何参数,设置参数的约束条件,可以通过with函数进行操作,该函数接受的参数其实也就是实际调用中对象的函数参数,在这里我们只是设定约束条件,如实际调用时的函数参数应和这里的参数相同、相等或类型相同等。如实际函数的声明如下getName(Integer para1,Integer para2),那么对应的with函数即可为with(new Constraint(para1Value),new Constraint(para2Value)),这样就可以保证参数约束的启用。with函数的返回值为MatchBuilder,这时我们可以设定我们希望的返回值,通过will函数就可以完成。这里函数,参数和返回值确定了一个匹配的原则,当模拟对象进行操作时,它需要找到对应的匹配,这样操作才能进行下去。此时我们就完成了在模拟对象上的一个期望实现。
JMock的设计思想非常好,这里其实就是以一个简单的实际例子来讲解,更多关于JMock的信息和功能你需要参考JMock的说明文档。在实际的测试过程中,我们还是要尽量避免使用模拟对象,毕竟我们要花费不少精力去模拟一个实际对象。如在Web开发过程中,不少人将HttpServletRequest作为函数参数,这本身不是什么问题,但是在测试方面讲,这有点麻烦,模拟一个能实现HttpServletRequest所有功能的类比较复杂,牵涉到web容器的方方面面,这里我们可以通过JMock来实现一个HttpServletRequest模拟对象,实现我们关心的一些方法,但是这不是上策。所以我们在设计的时候同时要考虑到测试,如果讲这两方面整合起来,对你的开发效率会有很大的提高。
总结:JMock的便捷性和思想非常好,上面我们的例子牵涉到许多类和方方面面,如果你的TestCase为JMock的MockObjectTestCase,那么你就方便多啦,MockObjectTestCase提供了许多方便的函数,简化我们的编码。关于模拟对象的使用,更多的是要从你的开发和经验中来,掌握好Mock将给你的测试带来意想不到的便捷。 |