1、Mock测试,Mock测试,单元测试的目标是一次只验证一个方法,但是倘若遇到这样的情况:某个方法依赖于其他一些难以操控的东西,诸如网络、数据库,甚至是servlet引擎,将会发生什么情况呢? 要是你的测试代码依赖于系统的其他部分,甚至系统的多个其他部分呢?在这种情况下,倘若不小心,你最终可能会发现自己几乎初始化了系统的每个组件,而这只是为了给某一个测试创造足够的运行环境让它可以运行起来。,Mock测试,例如开发一个税务管理系统,根据不同的价格上不同的税,税点可能依赖与另一个应用程序,而这个应用程序不在你这里,需要联网到税务局使用,而税务局的税点计算可能又依赖与当天银行的汇率,而银行的汇率又依赖
2、于当天石油的行情,对类似这样的程序做测试明显不可能等待程序正常启动,因为会耗费大量的时间和资源。 再例如,飞机和火箭的引擎测试都是在地面上实验的,并没有一边飞一边测试。,Mock测试,在电影和电视制作中,工作人员通常会为真正的演员提供一些替身( standins或doubles)具体来说,当摄影师在调整灯光或者摄像机角度的时候,他们会使用灯光替身:即高度和体形都与真正的演员相像,但地位并不那么重要的人,而那些身价不凡的真正的演员此时可能正在他们的拖车休息室里面懒洋洋地躺着呢。在单元测试中我们也要使用类似于电影中灯光替身一类的替代品:一种和真品非常接近的膺品,至少表面上看来是这样;但是就我们测试
3、的目的而言它更加好用。,Mock测试,我们需要做的是撇开真实世界中的实际对象,而以功能一样的盟友我们自制的“灯光替身”来代替它们。例如,我们可能并不想在真正的数据库上做测试,或者也不想使用当前墙上钟表显示的真实时间;让我们来看一个简单的例子吧。假设在代码中,你调用你自己的getTime()来返回系统当前的日期和时间。它的定义大概是这个样子:public long getTime()return System.currentTimeMillis();,Mock测试,一般而言,我们通常建议对应用程序范围外的功能调用进行包装,从而能够更好地封装它们上面就是一个很好的例子。因为在上面的代码中,我们是把
4、当前时间的概念包装在自己写的代码里面,所以调试就容易了一些: public long getTime()if(debug)return debug_cur_time;elsereturn System.currentTimeMillis(); ,Mock测试,这是替换真正功能的手段之一,但有些凌乱。首先,只有在代码一致调用你自己的getTime()、完全没有直接调用Java方法System .currentTimeMillis()的时候,这种方法才有效。但是我们需要的是一种更加干净并且更加面向对象化同时可以实现相同功能的方法。,Mock测试,一个模拟(mock)对象就是一个简单的接口或者是类,
5、在里面可以定义一个特定的方法调用之后的简单的输出(这里的输出是你自己给定的)以上面的税务管理为例,我们可能用税点来进行计算,只要给出特定税点能得到预期的结果就达到测试的目的,并不一定要真实的税点数据,这时候就可以在我们的mock类中直接给出假定税点而不用连接到税务局的程序中。屏蔽了复杂的操作。,Mock测试,mock对象也就是真实对象在调试期的替代品。在许多情况下,mock对象都可以给我们带来帮助。思考:什么情况需要用到mock测试?,Mock测试,真实对象具有不可确定的行为(产生不可预侧的结果,如股票行情)。 真实对象很难被创建。 真实对象的某些行为很难触发(如网络错误)。 真实对象令程序的
6、运行速度很慢。 真实对象有(或者是)用户界面。 测试需要询问真实对象它是如何被调用的(例如,测试可能需要验证某个回调函数是否被调用了)。 真实对象实际上并不存在(。当需要和其他开发小组,或者新的硬件系统打交道的时候,这是一个普遍问题)。,Mock测试,借助于mock对象,我们就可以解决上面提到的所有问题。在使用mock对象进行测试的时候,总共有3个关键步骤,分别是:1.使用一个接口来描述这个对象。2.为产品代码实现这个接口。3.以测试为目的,在mock对象中实现这个接口。,Mock测试,因为被测试代码只会通过接口来引用对象,所以它完全可以不知道它引用的究竟是真实对象还是mock对象。让我们再次
7、回顾一下上述的系统当前时间那个例子。我们将创建针对真实环境因素的几个对象,其中一个因素就是当前时间:public interface Environmentalpublic long getTime();/other methods omitted.,Mock测试,接下来,编写真实的实现代码:public class SYstemEnvironment implements Environmentalpublic long getTime()return System.currentTimeMillis();/other methods.,Mock测试,最后是mock的实现:public cl
8、ass MockSystemEnvironment implements Environmentalpublic long getTime()return current_time;public void setTIme(long aTime)current_time = aTime;private long current_time;,Mock测试,现在编写一个依赖于getTime()方法的新方法。 import java.util.Calendar; public class Checkerpublic Checker(Environmental anEnv)env = anEnv;/ 过
9、5点的时候,用铃声提醒人们可以回家了public void reminder()Calendar cal = Calendar.getInstance();cal.setTimeInMillis(env.getTIme();int hour = cal.get(Calendar.HOUR_OF_DAY);if(hour=17)/下午5点env.playWavFile(“quit_whistle.wav“);private Environmental env; ,Mock测试,在产品环境(卖给客户的真正的代码)中,当初始化这个类的对象时,传入的是一个真实的SystemEnvironmet;而另一
10、方面,测试代码传入的则是MockSystemEnvironmet。使用env.getTime()的被测试代码并不知道测试环境和真实环境之间的区别,因为它们都实现了相同的接口。现在,你可以借助mock对象,通过把时间设置为已知值,并检查行为是否如预期那样来编写测试了。,Mock测试,除了我们已经展示过的getTime , Environmental接口还有一个playWavFile()函数(在前面的Checker里面用到了的)。我们通过给mock对象添加一些额外的支持代码,从而能够在不倾听计算机喇叭的情况下,添加测试来观察它是否被调用了。,Mock测试,测试代码如下: package examp
11、le; import junit.framework.*; import java.util.Calendar; import example.eninterface.MockSystemEnvironment; public class TestChecker extends TestCase public void testQuittingTime()MockSystemEnvironment env = new MockSystemEnvironment();/设置一个目标测试时间16:55Calendar cal = Calendar.getInstance();cal.set(Cal
12、endar.YEAR, 2004);cal.set(Calendar.MONTH, 10);cal.set(Calendar.DAY_OF_MONTH, 1);cal.set(Calendar.HOUR_OF_DAY, 16);cal.set(Calendar.MINUTE, 55);long t1 = cal.getTimeInMillis();,Mock测试,env.setTIme(t1);Checker checker = new Checker(env);/运行checkerchecker.reminder();/应该还没有播放铃声assertFalse(env.wavWasPlaye
13、d();/设置时间经过5分钟t1 += (5*60*1000);env.setTIme(t1);/运行checkerchecker.reminder();,Mock测试,/这次应该播放铃声assertTrue(env.wavWasPlayed();/重置标志位,这样我们可以重新尝试别的情况env.resetWav();/设置时间经过2小时t1 += 2*60*60*1000;env.setTIme(t1);checker.reminder();assertTrue(env.wavWasPlayed(); ,Mock测试,代码创建了一个应用环境的mock版本。设置了将使用的假的时间,然后把它们设置给了mock环境对象。接下来调用了reminder(),使用mock环境。然后使用断言检查.wav文件是不是还没播放,因为在mock对象的环境中,此时还不是quitting time。但是我们将很快调整时间;把mock时间调整到刚好是quitting time。然后再一次调用reminder()函数。这次,声音已经被播放过了,因而我们在接下来的断言确认了.wav文件这次已经播放过了。最终,我们重设mock环境的.wav文件的标志,并且测试再过两个小时之后的情况。,