moq(发音"Mock-you" 或 "Mock")是.net的模拟框架,它从开始开发就充分利用.net3.5(如:linq表达式树)和C#3.0(如:lambda表达式)的特性,使它成为最富有创造力、类型安全、友好重构的模拟框架成为可能。它既支持模拟接口又支持模拟类。它的API简洁明了,使用模拟的概念预先不需要学习任何知识和经验。

什么是Moq-程序旅途
蒙娜丽莎的微笑

上面一段话是英文的原话翻译,读完之后可以了解到moq是一个模拟框架,而且使用起来非常简单。但是,moq到底是什么,它适用于何种情况能够解决什么不得而知。下面就揭开moq小姐的面纱。

有位先生对moq小姐比较了解,他告诉我moq是用来伪装和隔离被依赖的对象,从而提高被测对象的测试效果。这位先生比较高深,而我只是个小小的程序猿,思考半天才明白了一些。在单元测试中,加桩(为了让一个模块执行起来,额外添加的一些支撑代码)是无法避免的,不使用moq(或其它模拟框架)就需要手动加桩。单元测试中另一个比较常见的问题就是数据的持久化,比如,专注于业务逻辑,数据持久化还没有实现,即使实现了,也不想单元测试影响到数据库的内容。下面我用代码来解释上面的话。

最近城管执法比较暴力,还打死了人,打死人当然要被警察叔叔刑拘的,下面的代码就是关于此的场景。

首先建立一个类库项目(WhatIsMoq.Model)和测试项目(WhatIsMoq.Tests),在WhatIsMoq.Model项目中新建一个IEnforceLaw接口以及PoliceEnforceLaw和ChenGuanEnforceLaw类分别实现IEnforceLaw接口。具体代码如下所示:

    /// <summary>
    /// 执法接口
    /// </summary>
    public interface IEnforceLaw
    {
        //杀人
        bool KillPeople();
        //抓人
        bool GraspPeople();
    }

    //警察执法
    public class PoliceEnforceLaw : IEnforceLaw
    {
        //警察执法依赖于城管执法,因为城管杀了人,警察才抓城管
        IEnforceLaw _chengguanEnforceLaw;

        public PoliceEnforceLaw(IEnforceLaw chengguanEnforceLaw)
        {
            _chengguanEnforceLaw = chengguanEnforceLaw;
        }

        public bool KillPeople()
        {
            return false;
        }

        //城管杀了人,警察抓城管
        public bool GraspPeople()
        {
            return _chengguanEnforceLaw.KillPeople();
        }
    }

    //城管执法(没有具体实现)
    public class ChenGuanEnforceLaw : IEnforceLaw
    {
        public bool KillPeople()
        {
            throw new NotImplementedException();
        }

        public bool GraspPeople()
        {
            throw new NotImplementedException();
        }
    }

由于城管执法没有具体实现,在对警察执法单元测试时,由于它依赖于城管执法,因此单元测试无法进行,这个时候就需要添加额外的代码(加桩)来支撑单元测试。考虑到城管KillPeople有true和false两种情况,因此,要添加两个城管执法的加桩代码。如下代码是城管执法的加桩代码:

    //城管执法返回KillPeople返回True的加桩代码
    public class MockChenGuanEnforceLawKillPeopleReturnTrue : IEnforceLaw
    {
        public bool KillPeople()
        {
            return true;
        }

        public bool GraspPeople()
        {
            throw new NotImplementedException();
        }
    }

    //城管执法返回KillPeople返回False的加桩代码
    public class MockChenGuanEnforceLawKillPeopleReturnFalse : IEnforceLaw
    {
        public bool KillPeople()
        {
            return false;
        }

        public bool GraspPeople()
        {
            throw new NotImplementedException();
        }
    }

下面来写两个测试用例,如下所示:

    [TestClass]
    public class WhatIsMoqTest
    {
        //警察抓城管测试用例
        [TestMethod]
        public void Police_Grasp_ChengGuan()
        {
            PoliceEnforceLaw pel = new PoliceEnforceLaw(new MockChenGuanEnforceLawKillPeopleReturnTrue());
            var grasp = pel.GraspPeople();
            Assert.AreEqual(true, grasp);
        }

        //警察不抓城管测试用例
        [TestMethod]
        public void Police_Not_Grasp_ChengGuan()
        {
            PoliceEnforceLaw pel = new PoliceEnforceLaw(new MockChenGuanEnforceLawKillPeopleReturnFalse());
            var grasp = pel.GraspPeople();
            Assert.AreEqual(false, grasp);
        }
    }

通过上面的例子可以看到,如果没有模拟框架,就需要自己来写加桩代码,反之,如果利用moq模拟框架,则可以大大简化开发效率。下面来看一下使用moq的测试代码:

        Mock<IEnforceLaw> chengguanEnforceLaw;

        //创建城管执法的模拟对象
        [TestInitialize]
        public void Create_ChengGuan_EnforceLaw_Mock()
        {
            chengguanEnforceLaw = new Mock<IEnforceLaw>();
        }

        //使用Moq的警察抓城管测试用例
        [TestMethod]
        public void Police_Grasp_ChengGuan_Test_With_Moq()
        {
            //var chengguanEnforceLaw = new Mock<IEnforceLaw>();
            chengguanEnforceLaw.Setup(t => t.KillPeople()).Returns(true);
            PoliceEnforceLaw pel = new PoliceEnforceLaw(chengguanEnforceLaw.Object);
            var grasp = pel.GraspPeople();
            Assert.AreEqual(true, grasp);
        }

        //使用Moq的警察不抓城管测试用例
        [TestMethod]
        public void Police_Not_Grasp_ChengGuan_Test_With_Moq()
        {
            //var chengguanEnforceLaw = new Mock<IEnforceLaw>();
            chengguanEnforceLaw.Setup(t => t.KillPeople()).Returns(false);
            PoliceEnforceLaw pel = new PoliceEnforceLaw(chengguanEnforceLaw.Object);
            var grasp = pel.GraspPeople();
            Assert.AreEqual(false, grasp);
        }

下面来看一下单元测试的结果:

什么是Moq-程序旅途
单元测试的结果

本篇文章的前面部分解释了什么是moq,能干什么,后面部分给出了一个例子助于理解,现在终于可以看到moq小姐的真面纱了。

本文源代码下载地址:

链接:http://pan.baidu.com/share/link?shareid=3631839997&uk=2466437167 密码:c64f