C# (.NET) 测试:框架与工具概述
高质量的测试是 .NET 平台上可靠软件的基础。C# 生态系统中有多个成熟的框架可用于单元测试、集成测试和验收测试。在本文中,我们将分析主要工具、它们的特点,并提供实际代码示例。
单元测试的主要框架
在 .NET Core 和 .NET 5+ 中,内置的测试支持通过三个主要框架实现:
- MSTest — 微软的经典框架,对初学者友好。
- NUnit — 流行的开源框架,拥有丰富的属性集。
- xUnit.net — 现代、轻量且灵活的工具,由 NUnit 的作者创建。
这三个框架都在 Visual Studio、Rider 和通过 dotnet CLI 中得到支持。选择取决于团队的偏好,但 xUnit 和 NUnit 被认为在复杂场景下更灵活。
xUnit 测试示例
让我们看看如何使用 xUnit 测试一个简单的计算器:
using Xunit;
public class Calculator{ public int Add(int a, int b) => a + b; public int Divide(int a, int b) => b == 0 ? throw new DivideByZeroException() : a / b;}
public class CalculatorTests{ [Fact] public void Add_ShouldReturnSum_WhenTwoNumbers() { // Arrange var calc = new Calculator();
// Act var result = calc.Add(3, 5);
// Assert Assert.Equal(8, result); }
[Theory] [InlineData(10, 2, 5)] [InlineData(9, 3, 3)] [InlineData(7, 1, 7)] public void Divide_ShouldReturnQuotient(int a, int b, int expected) { var calc = new Calculator(); var result = calc.Divide(a, b); Assert.Equal(expected, result); }
[Fact] public void Divide_ShouldThrow_WhenDivideByZero() { var calc = new Calculator(); Assert.Throws<DivideByZeroException>(() => calc.Divide(5, 0)); }}[Fact] 属性表示一个普通测试,而 [Theory] 与 [InlineData] 允许使用不同的输入数据运行同一个测试。
模拟对象工具
在进行单元测试时,通常需要将被测试类与外部依赖项(数据库、API、文件系统)隔离。为此,可以使用模拟框架:
- Moq — 最流行,语法简单,文档完善。
- NSubstitute — 更简洁,具有流畅接口。
- FakeItEasy — 友好的 API,用于快速创建存根。
Moq 使用示例
假设我们有一个发送电子邮件的服务。我们想在不实际发送的情况下测试逻辑:
using Moq;using Xunit;
public interface IEmailSender{ void Send(string to, string subject, string body);}
public class NotificationService{ private readonly IEmailSender _sender; public NotificationService(IEmailSender sender) => _sender = sender;
public void NotifyUser(string email, string message) { if (string.IsNullOrWhiteSpace(email)) throw new ArgumentException("Email is required"); _sender.Send(email, "Notification", message); }}
public class NotificationServiceTests{ [Fact] public void NotifyUser_ShouldCallSend_WhenValidEmail() { // Arrange var mockSender = new Mock<IEmailSender>(); var service = new NotificationService(mockSender.Object);
// Act service.NotifyUser("test@example.com", "Hello!");
// Assert mockSender.Verify(s => s.Send("test@example.com", "Notification", "Hello!"), Times.Once); } [