单元测试——使用模拟对象做交互测试

最近在看.net单元测试艺术,我也喜欢单元测试,这里写一下如何在测试中使用模拟对象。

开发的过程中,我们都会遇到对象间的依赖,比如依赖数据库或文件,这时,我们需要使用模拟对象,来进行测试,我们可以手写模拟对象,当然也可以使用模拟框架。

假如有这样的一个需求,当用户登陆时,我需要对用户名和密码进行验证,然后再将用户名写入日志中。


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

public class MyLogin

    {

        public ILog Log { getset; }

 

        public bool Valid(string userName, string passWord)

        {

            var isValid = userName == "admin" && passWord == "123456";

            Log.Write(userName);

            return isValid;

        }

    }

    public interface ILog

    {

        void Write(string message);

   }

}

上面的代码在验证完登陆信息后,需要向日志中写入用户名,由于写入日志可能依赖于文件或数据库,我们可能很难进行测试,所以,这里使用模拟对象进行测试。手写模拟对象,代码如下:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

[TestFixture]

    public class MyLoginTest

    {

        [Test]

        public void Vaild_Test()

        {

            MyLogin login = new MyLogin();

 

            var log = new TestLog();

 

            login.Log = log;

            var userNmae = "admin";

            var passWord = "123456";

            var isLogin = login.Valid(userNmae, passWord);

            Assert.AreEqual(isLogin, true);

            Assert.AreEqual(log.Message, userNmae);

        }

   }

    public class TestLog : ILog

    {

        public string Message;

        public void Write(string message)

        {

            this.Message = message;

        }

  }

这里我们定义了一个对象TestLog对象,该对象就是一个模拟对像,继承了ILog接口。该测试中,一共进行了两项测试。一项是:验证用户名和密码是否输入正确。另一项是:验证用户写入日志的信息是否正确(比如应该写入用户名,结果把密码写入了日志,测试会无法通过)。

这里我们区分一下模拟对象与桩对象。上一节中,我们讲过桩对象的定义,那么模拟对象与桩对象是什么关系呢?

模拟对象与桩对象在写法上区别很小,关键在于模拟对象需要进行断言,也就是说模拟对象可以导致测试失败。桩对象只是为了方便测试所定义的一个对象,不需要进行断言,所以,桩对象永远不会导致测试失败。

上面的测试中,如果我们去掉最后一行代码,即我们不进行写入日志的断言,则该对象就是一个桩对象。


1

Assert.AreEqual(log.Message, userNmae);

  上面的模拟对象是我们自己写的,自己写模拟对象比较费时,我们可以使用模拟框架进行编写。这里我使用了Rhino Mocks框架。如果要执行下面的代码,需要下载Rhino.Mocks.dll文件,然后直接引用即可。

测试框架这里我选用了NUnit框架。测试代码如下:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

[TestFixture]

    public class MyLoginTest

    {

        [Test]

         public void Mock_Vaild_Test()

        {

            MockRepository mock = new MockRepository();

            var log = mock.DynamicMock<ILog>();

            var userName = "admin";

            var passWord = "123456";

            using (mock.Record())

            {

                log.Write(userName);

            }

            MyLogin login = new MyLogin();

            login.Log = log;

            var isLogin = login.Valid(userName, passWord);

            Assert.AreEqual(isLogin, true);

            mock.VerifyAll();

        }

  这里我没有编写一个类去继承ILog接口,而是通过模拟框架,动态生成了一个ILog对象。代码是这句:


1

2

3

MockRepository mock = new MockRepository();

var log = mock.DynamicMock<ILog>();

  这里便生成了Log对象。通过录制-回放的模式进行模拟对象测试,首先需要定义我们的期望行为,最后验证实际行为与期望行为是否一致。这里,需要录制我们期望行为,代码如下:


1

2

3

4

using (mock.Record())

     {

         log.Write(userName);

     }

  这里我们期望向日志中写入用户名。再通过回放来进行验证,代码如下:


1

mock.VerifyAll();

  该方法会验证,期望向日志中写入的信息与实际向日志中写入的信息是否一致,如果不一致,测试失败。

  这里我们便完成了使用模拟框架进行单元测试。如果我们不需要测试日志写入方法,则把模拟对象换成桩对象就可以了,生成桩对象的方法如下:


1

2

3

MockRepository mock = new MockRepository();

var log = mock.Stub<ILog>();

把回放的方法(mock.VerifyAll())去掉,就完成了模拟对象向桩对象的转变。注意,这里录制的代码还是需要的。

总结:编写模拟对象和桩对象是非常有意义的,使用框架可以帮助我们简化单元测试。一般情况下,一个测试中,可以有多个桩对象,但最好只有一个模拟对象。模拟对象太多,证明一个测试方法做了太多项测试,不利于维护测试代码,一旦代码变改,很容易使单元测试失败。

下一节,写一下测试框架的一些常用功能,如:如何模拟异常、如何模拟返回值等。。。

时间: 2024-10-27 03:45:20

单元测试——使用模拟对象做交互测试的相关文章

Android单元测试与模拟测试详解

测试与基本规范 为什么需要测试? 为了稳定性,能够明确的了解是否正确的完成开发. 更加易于维护,能够在修改代码后保证功能不被破坏. 集成一些工具,规范开发规范,使得代码更加稳定( 如通过 phabricator differential 发diff时提交需要执行的单元测试,在开发流程上就可以保证远端代码的稳定性). 2. 测什么? 一般单元测试: 列出想要测试覆盖的异常情况,进行验证. 性能测试. 模拟测试: 根据需求,测试用户真正在使用过程中,界面的反馈与显示以及一些依赖系统架构的组件的应用测

模拟对象测试——EasyMock

一.EasyMock 使用动态代理实现模拟对象创建,一般可以满足以下测试需求 1.要测试的模块依赖于其它自己控制不了的模块,如第三方服务,其它组员在开发的服务等,它们都没办法配合你来测试: 2.涉及到数据库操作,但当时条件连接不了数据库或你不想将自己的测试结果改动到数据库: 3.模块依赖于spring容器注入其它服务实例,但你又不想启动spring容器. 总之,EasyMock能帮我们模拟出任何我们想要的支持对象,并且我们可以使用这些对象进行逻辑操作 二.具体使用方法 1.模拟成员变量 Easy

单元测试(二)-桩对象

在单元测试时,难免会碰到一些外部依赖,外部依赖是指在系统中代码与其交互的对象,而且无法对其做人为控制,比如文件系统.线程.内存.时间.数据库结果集等,这时可以使用伪对象(fake)来替代外部依赖,桩对象(stub)便是其中之一 一 桩对象 a) 桩对象是对系统中现有依赖项的一个替代品,可人为控制.通过使用桩对象,无需涉及依赖项,即可直接对代码进行测试.使用桩对象可以轻松地控制模拟依赖项的返回值会行为(比如模拟内存溢出异常). b) 使用桩对象的前提是要找到原系统中的接缝(Seam).接缝是指代码

打破依赖,使用模拟对象,桩对象,隔离框架

打破依赖,使用模拟对象,桩对象,隔离框架 在上节中,完成了第一个单元测试,研究了各种特性,在本节,将介绍一些更实际的例子.SUT依赖于一个不可操控的对象,最常见的例子是文件系统,线程,内存和时间等. 本系列将分成3节: 单元测试基础知识 打破依赖,使用模拟对象,桩对象,隔离框架 创建优秀的单元测试 本节索引: 伪对象.桩对象. 模拟对象 为什么需要伪对象,如何处理 手工新建伪对象 使用隔离框架创建伪对象 伪对象(fake) 桩对象(stub) 模拟对象(mock) 伪对象是一个通用术语,它即可指

使用uiautomator做UI测试

转载~~~~~~~~~~~~~~~~~~~~~~~~ 若有侵权,请及时联系本博主,博主将第一时间撤销 在Android 4.1发布的时候包含了一种新的测试工具–uiautomator,uiautomator是用来做UI测试的.也就是普通的手工测试,点击每个控件元素 看看输出的结果是否符合预期.比如 登陆界面 分别输入正确和错误的用户名密码然后点击登陆按钮看看是否能否登陆以及是否有错误提示等. 功能性或者黑盒UI测试不需要测试人员了解程序如何实现的,只需要验证各种操作的结果是否符合预期即可.这样的

[zhuan]使用uiautomator做UI测试

http://blog.chengyunfeng.com/?p=504 在Android 4.1发布的时候包含了一种新的测试工具–uiautomator,uiautomator是用来做UI测试的.也就是普通的手工测试,点击每个控件元素 看看输出的结果是否符合预期.比如 登陆界面 分别输入正确和错误的用户名密码然后点击登陆按钮看看是否能否登陆以及是否有错误提示等. 功能性或者黑盒UI测试不需要测试人员了解程序如何实现的,只需要验证各种操作的结果是否符合预期即可.这样的测试可以分离团队的开发人员和测

如何用Jmeter做压力测试 --- 转

Jmeter是一个性能测试工具,同loadrunner类似,他功能较多,我们常用的功能是用jmeter模拟多浏览器对网站做压力测试.    下载jmeter地址 :http://jakarta.apache.org/我们一般的网站,在进入业务功能前先需登录,然后才能访问业务功能.下面介绍如何用jmeter登录系统再对主业务做压力测试.1 运行jmeter    2 左边树将出现测试计划.工作台两根节点.3 选择测试计划,按右键->添加->threads(users)线程组    线程组能设置以

(转)学习使用Jmeter做压力测试(三)--数据库测试

数据库测试 JMeter可以做为Web服务器与浏览器之间的代理网关,以捕获浏览器的请求和Web服务器的响应,这样就可很容易的生成性能测试脚本. 根据脚本,JMeter可通过线程组来模拟真实用户对Web服务器做压力测试.本文描述使用JMeter进行数据库测试的过程.创建测试计划,模拟 并发用户发送SQL请求到数据库.测试数据库. 性能测试的目标是找到系统的性能瓶颈.本文将通过构造测试场景,完成对数据库的测试. 场景: 1.单用户: a.SQL语句优化:    b.数据库约束检查:   c.分页查询

【转】学习使用Jmeter做压力测试(三)--数据库测试

JMeter可以做为Web服务器与浏览器之间的代理网关,以捕获浏览器的请求和Web服务器的响应,这样就可很容易的生成性能测试脚本.根据脚本,JMeter可通过线程组来模拟真实用户对Web服务器做压力测试.本文描述使用JMeter进行数据库测试的过程.创建测试计划,模拟并发用户发送SQL请求到数据库.测试数据库. 性能测试的目标是找到系统的性能瓶颈.本文将通过构造测试场景,完成对数据库的测试. 场景: 1.单用户: a.SQL语句优化:    b.数据库约束检查:   c.分页查询: 2.并发用户