C# FakeItEasy
上次修改时间:2023 年 9 月 2 日
在本文中,我们将展示如何使用 FakeItEasy 库在 C# 中进行模拟。
模拟 (Faking) 是用伪造对象 (fakes) 或测试替身 (test doubles) 替换外部依赖项。 这些类或组件在单元测试中模拟成功或失败的操作。
使用伪造对象主要是因为外部依赖项当前可能无法用于测试,或者它们的使用成本非常高。
FakeItEasy 是一个易于使用的 .NET 模拟库。
$ dotnet add package FakeItEasy $ dotnet add package Microsoft.NET.Test.Sdk $ dotnet add package MSTest.TestAdapter $ dotnet add package MSTest.TestFramework
在本文中,我们使用 MSTest 测试框架。
C# FakeItEasy 简单示例
在第一个示例中,我们用伪造对象替换一个简单的 HelloService 类。
namespace Messages.Services;
public interface IMessageService
{
string GetHelloMessage();
string GetGreetingMessage();
}
FakeItEasy 从 IMessageService 接口创建伪造对象。
namespace Messages.Services;
public class MessageService : IMessageService
{
public string GetHelloMessage()
{
return "Hello there!";
}
public string GetGreetingMessage()
{
return "Good Morning!";
}
}
MessageService 是 IMessageService 的实现。
namespace Messages.Tests;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using FakeItEasy;
using Messages.Services;
[TestClass]
public class MessageServiceTest
{
private const string Expected1 = "Hello there!";
private const string Expected2 = "Good Morning!";
[TestMethod]
public void HelloMessageTest()
{
var msgService = A.Fake<IMessageService>();
A.CallTo(() => msgService.GetHelloMessage()).Returns(Expected1);
var res = msgService.GetHelloMessage();
Assert.AreEqual(Expected1, res);
}
[TestMethod]
public void GreetingMessageTest()
{
var msgService = A.Fake<IMessageService>();
A.CallTo(() => msgService.GetGreetingMessage()).Returns(Expected2);
var res = msgService.GetGreetingMessage();
Assert.AreEqual(Expected2, res);
}
}
在 MessageServiceTest 中,我们测试 MessageService 类。
var msgService = A.Fake<IMessageService>(); A.CallTo(() => msgService.GetHelloMessage()).Returns(Expected1);
我们从 IMessageService 接口创建一个伪造的 MessageService,并为 GetHelloMessage 调用定义一个响应。
var res = msgService.GetHelloMessage(); Assert.AreEqual(Expected1, res);
该方法使用伪造对象而不是实际类进行测试。
C# FakeItEasy 示例 II
由于我们有一个带有测试框架的控制台程序,我们需要将以下选项添加到项目文件中。
<GenerateProgramFile>false</GenerateProgramFile>
否则,我们将有两个主要的入口点发生冲突。
$ dotnet add package Bogus
除了前面提到的包之外,我们还添加了 Bogus 库来创建伪造数据。
namespace Users.Models;
public class User
{
public User(string fname, string lname, string occupation) =>
(FirstName, LastName, Occupation) = (fname, lname, occupation);
public string FirstName { get; set; }
public string LastName { get; set; }
public string Occupation { get; set; }
public override string ToString() => $"{FirstName} {LastName} is a {Occupation}";
}
这是 User 类。
namespace Users.Services;
using Users.Models;
public interface IUserService
{
User GetUser();
IList<User> GetUsers(int n);
}
我们有一个 IUserService 接口,其中包含两个合约方法。 GetUser 返回单个用户,GetUsers 返回 n 个用户。
namespace Users.Services;
using Users.Models;
using Bogus;
public class UserService : IUserService
{
private List<string> occupations = new List<string> { "teacher",
"programmer", "driver", "accountant" };
public IList<User> GetUsers(int n)
{
var users = new List<User>();
foreach (int value in Enumerable.Range(1, n))
{
users.Add(CreateUser());
}
return users;
}
public User GetUser()
{
var user = CreateUser();
return user;
}
public User CreateUser()
{
var faker = new Faker();
var fname = faker.Person.FirstName;
var lname = faker.Person.LastName;
int n = occupations.Count();
int millis = DateTime.Now.Millisecond;
var occupation = occupations.ElementAt(new Random(millis).Next(n));
return new User(fname, lname, occupation);
}
}
我们的 UserService 实现了合约方法。 它使用 Bogus 为用户生成伪造数据。
namespace Main;
using Users.Services;
public class Program
{
public static void Main(string[] args)
{
var userService = new UserService();
var u1 = userService.GetUser();
Console.WriteLine(u1);
var users = userService.GetUsers(5);
Console.WriteLine(string.Join("\n", users));
}
}
这是使用 userService 的主控制台程序。
namespace UserService.Tests;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using FakeItEasy;
using Users.Services;
using Users.Models;
[TestClass]
public class MessageTest
{
[TestMethod]
public void GetUserTest()
{
var userService = A.Fake<IUserService>();
var dummyUser = A.Dummy<User>();
A.CallTo(() => userService.GetUser()).Returns(dummyUser);
var res = userService.GetUser();
Assert.AreEqual(dummyUser, res);
}
[TestMethod]
[DataRow(2)]
[DataRow(5)]
[DataRow(10)]
public void GetUsersTest(int n)
{
var userService = A.Fake<IUserService>();
var dummyUsers = A.CollectionOfFake<User>(n);
A.CallTo(() => userService.GetUsers(n)).Returns(dummyUsers);
var res = userService.GetUsers(n);
Assert.AreEqual(dummyUsers, res);
}
}
我们使用测试替身测试 UserService 类。
var userService = A.Fake<IUserService>();
首先,我们创建一个伪造的 UserService。
var dummyUser = A.Dummy<User>(); A.CallTo(() => userService.GetUser()).Returns(dummyUser);
然后我们定义一个虚拟用户,并将其设置为 GetUser 方法调用的响应。
var res = userService.GetUser(); Assert.AreEqual(dummyUser, res);
最后,我们使用测试替身测试 GetUser 方法。
[TestMethod]
[DataRow(2)]
[DataRow(5)]
[DataRow(10)]
public void GetUsersTest(int n)
{
var userService = A.Fake<IUserService>();
var dummyUsers = A.CollectionOfFake<User>(n);
A.CallTo(() => userService.GetUsers(n)).Returns(dummyUsers);
var res = userService.GetUsers(n);
Assert.AreEqual(dummyUsers, res);
}
使用 MSTest 的 DataRow,我们依次使用值 2、5 和 10 运行 GetUsers 方法。
来源
在本文中,我们已经用 C# 中的 FakeItEasy 将两个实际类替换为测试替身。
作者
列出所有 C# 教程。