目录
介绍
返回语句以返回值
执行某些函数后执行某些任务
从模拟函数顺序返回多个值
第二次抛出异常
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();
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();
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();
//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();
//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);
}
}
}
我们看到它在第二次调用中引发异常。
当我们要调用原始函数而不是模拟函数时,此设置很有用。在此示例中,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();
mock.CallBase = true;
mock.SetupSequence(x => x.Function()).CallBase();
Assert.AreEqual(mock.Object.Function(), true);
}
}
}
并且引发Function()的异常。
泛型类的模拟机制就像普通类的模拟一样。看下面的例子:
public class Hello
{
}
public class TestClass where T : class
{
public virtual Boolean Function()
{
throw new Exception();
}
}
[TestClass]
public class MVCUnitTest
{
[TestMethod]
public void MockAlways()
{
var mock = new Mock();
mock.SetupSequence(x => x.Function()).Returns(true);
Assert.AreEqual(mock.Object.Function(), true);
}
}
在本文中,我们学习了使用Moq框架的一些重要的模拟设置。在下一篇文章中,我打算更深入地研究模拟。