使用Moq框架的各种模拟设置

目录

介绍

返回语句以返回值

执行某些函数后执行某些任务

从模拟函数顺序返回多个值

第二次抛出异常

CallBase()调用原始实现

模拟泛型类

边界线


介绍

在本文中,我们将了解使用Moq框架的各种模拟设置。基本上,这些设置有助于应用程序的单元测试。如果您有单元测试的经验,那么您可能已经知道这些概念。我们知道模拟是一种我们用自定义或伪造操作模仿原始操作的操作。在应用程序开发时,有时我们会看到一个组件依赖于另一个组件,并且我们不能等到依赖对象完成。

在这种情况下,模拟的概念就出现了。模拟对象将模仿原始对象,以便我们可以继续进行开发过程。市场上有许多模拟框架可用于创建模拟对象。Moq就是其中之一。它是免费且易于使用的。在本文中,我们将使用Moq作为我们的模拟框架。在进行模拟设置时,可能需要在单元测试配置期间实现不同的情况。在此示例中,我们将了解Moq框架的一些重要设置。

首先,为您的应用程序提供Moq框架参考。提供参考后,它将显示在解决方案的参考文件夹中,如下所示。

因此,让我们从第一个配置开始。

返回语句以返回值

我们可以为函数设置期望的返回值。在此示例中,我们将使用模拟对象设置Hello()函数,然后进行设置,以使Hello()函数执行后始终返回“true”。在这里,true是原始类型值。如果需要,我们也可以返回自定义复杂类型。请注意,由于Moq要求,我们已将Hello()函数声明为虚函数。当我们要模拟一个具体的实现时,该函数应该定义为虚函数。看下面的代码。

namespace TestMVC
{
    public class TestClass
    {
        public virtual Boolean Hello()
        {
            throw new Exception();
        }
    }

    [TestClass]
    public class MVCUnitTest
    {
        [TestMethod]
        public void MockAlways()
        {
            var mock = new Mock<TestClass>();
            mock.Setup(x => x.Hello()).Returns(true);
            Assert.AreEqual(mock.Object.Hello(), true);
        }
    }
}

执行某些函数后执行某些任务

这是另一个非常重要的设置。在其他操作完成或某些函数执行之后,有时需要执行某些操作。例如,我们要计算函数执行的次数,并将评估这些次数以影响我们的决策。在这种情况下,我们可以在模拟时进行设置callback()。这是一个示例示例。

[TestClass]
    public class MVCUnitTest
    {
        [TestMethod]
        public void MockAlways()
        {
            string status = "";
            var mock = new Mock<Service>();
            mock.Setup(x => x.CallService()).Returns(true).Callback(() => { 
                //Do some other stuff
                status = "FunctionCalled"; 
            });

            var consumer = new ServiceConsumer(mock.Object);
            Assert.AreEqual(consumer.Execute(), true);
            if (status == "FunctionCalled")
            {
                //perform other task when finish the first 
            }
        }
    }

一旦完成CallService()函数的执行,它将立即执行回调并执行其他一些操作。在此示例中,我们只是在设置一些变量值,它可能会进一步检查以在其他步骤中做出决定。

从模拟函数顺序返回多个值

这是另一个重要的设置,其中,模拟函数(我的意思是与模拟对象关联的函数设置)将在每个调用中返回不同的值。这是一个简单的实现:

using System;
using ConsoleApp;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using ConsoleApp;
using System.Collections.Generic;

namespace TestMVC
{

    public class TestClass
    {
        public virtual Boolean ReturnSequence()
        {
            throw new Exception();
        }
    }

    [TestClass]
    public class MVCUnitTest
    {
        [TestMethod]
        public void MockAlways()
        {
            var mock = new Mock<TestClass>();
            
            //First Return True then false
            mock.SetupSequence(x => x.ReturnSequence())
                .Returns(true)
                .Returns(false);
            Assert.AreEqual(mock.Object.ReturnSequence(), true);
            Assert.AreEqual(mock.Object.ReturnSequence(), false);

        }
    }
}

在此示例中,我们使用了多个Returns()语句。第一次它将返回true,而下次将返回false。这是输出,正如我们所期望的,我们看到测试正在通过。

第二次抛出异常

在某些情况下,我们可能需要一种配置,使模拟函数第一次返回一个值,但是如果第二次调用它将抛出异常。在此示例中,该函数在第一次调用时将返回true,而在第二次调用时将引发异常。

namespace TestMVC
{

    public class TestClass
    {
        public virtual Boolean Function()
        {
            throw new Exception();
        }
    }

    [TestClass]
    public class MVCUnitTest
    {
        [TestMethod]
        public void MockAlways()
        {
            var mock = new Mock<TestClass>();
            
            //First Return True then Throws exception
            mock.SetupSequence(x => x.Function())
                .Returns(true)
                .Throws(new Exception());

            Assert.AreEqual(mock.Object.Function(), true);
            Assert.AreEqual(mock.Object.Function(), true);

        }
    }
}

我们看到它在第二次调用中引发异常。

CallBase()调用原始实现

当我们要调用原始函数而不是模拟函数时,此设置很有用。在此示例中,Function()由于没有被模拟,我们借助CallBase()调用原始函数。当我们的Function()故意抛出异常时,测试应该抛出异常。

namespace TestMVC
{
    public class TestClass
    {
        public virtual Boolean Function()
        {
            throw new Exception();
        }
    }

    [TestClass]
    public class MVCUnitTest
    {
        [TestMethod]
        public void MockAlways()
        {
            var mock = new Mock<TestClass>();
            mock.CallBase = true;

            mock.SetupSequence(x => x.Function()).CallBase();
                

            Assert.AreEqual(mock.Object.Function(), true);

        }
    }
}

并且引发Function()的异常。

模拟泛型类

泛型类的模拟机制就像普通类的模拟一样。看下面的例子:

public class Hello
   {
   }
   public class TestClass <T> where T : class
   {
       public virtual Boolean Function()
       {
           throw new Exception();
       }
   }
   [TestClass]
   public class MVCUnitTest
   {
       [TestMethod]
       public void MockAlways()
       {
           var mock = new Mock<TestClass<Hello>>();
           mock.SetupSequence(x => x.Function()).Returns(true);
           Assert.AreEqual(mock.Object.Function(), true);
       }
   }

边界线

在本文中,我们学习了使用Moq框架的一些重要的模拟设置。在下一篇文章中,我打算更深入地研究模拟。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值