发布于 2026-01-06 0 阅读
0

.NET 中的单元测试:工具和技术

.NET 中的单元测试:工具和技术

介绍

想象一下你在搭建乐高积木。随着积木的不断添加,你希望确保每一块新积木都能完美契合,并且不会影响整个结构的稳固性。在软件开发中,单元测试就像是在添加每一块乐高积木之前进行检查,以确保整个结构保持稳固。

单元测试是一项基础实践,它能帮助我们确保代码的每个部分都能正确运行。在本文中,我将尝试解释什么是单元测试、它为何如此重要,以及如何使用简单的工具和技术在 .NET 中执行单元测试。

什么是单元测试?

单元测试是指对代码的各个单元(例如函数或方法)进行测试,以验证它们是否按预期运行。可以将单元测试想象成一个小型自动化检查清单,用于确认程序中的特定部分是否正常工作。

例如,如果你有一个计算两个数相除的方法,单元测试会检查该方法在给定特定输入时是否返回正确的结果。

为什么单元测试很重要?

让我们换个比喻,想象一下我们正在制造一辆汽车。我们需要测试各个部件,例如发动机、刹车、车灯等等,以确保它们各自都能正常工作。单元测试在软件开发中也起着类似的作用。

这之所以重要,原因有很多,但最重要的原因如下:

  • 及早发现缺陷,就像及早发现汽车故障部件一样,单元测试可以帮助我们在代码错误演变成更严重的问题之前就检测到它们。
  • 更易于维护:随着我们在应用程序单元测试中不断添加或更改功能,可以确保新的更改不会破坏现有功能。
  • 对代码的信心,单元测试让我们确信代码能够按预期运行,从而更容易重构和扩展应用程序。

.NET 单元测试入门

理论就讲到这里,让我们来看一个实际例子吧。毕竟,众所周知,我们开发者更喜欢代码而不是相关的理论😁

我的测试将使用 xUnit 框架。

  1. 在 .NET 中设置单元测试项目非常简单。当我们在常用的 IDE 中创建新项目时,就可以同时添加一个单元测试项目。
    • 创建一个新项目,在我们的 IDE 中,我们选择创建一个新项目,然后我们必须选择所需的框架“xUnit”或“NUnit”,目前这些是 .NET 中最流行的测试框架。
    • 添加引用,确保单元测试项目引用包含要测试代码的项目。
  2. 编写我们的第一个单元测试。假设我们的代码中有一个简单的方法,用于计算两个数的除法。
public class Calculator
{
    public int Div(int numerator, int denominator)
    {
        return numerator / denominator;
    }
}
Enter fullscreen mode Exit fullscreen mode

为了测试这种方法,我们将编写如下所示的单元测试:

public class CalculatorTests
{
    [Fact]
    public void Div_ReturnsCorrectResult()
    {
        // Arrange
        Calculator calculator = new Calculator();

        int numerator = 10;
        int denominator = 2;

        // Act
        int result = calculator.Div(numerator, denominator);

        // Assert
        Assert.Equal(5, result);
    }
}
Enter fullscreen mode Exit fullscreen mode

让我们把它分解成几个部分,以便更好地理解:

首先,正如您所见,我特意将测试分为三个部分:Arrange(安排)Act(执行)Assert(断言)。这样做的原因是,在我们的测试中,使用 AAA 模式被认为是一种良好的实践。这样,我们可以更好地组织测试,使其更易于阅读和理解。

  • 在本节中,我们需要准备测试所需的任何对象或数据,例如创建计算器对象的实例或为numerator变量赋值denominator
  • 在这里,我们调用要测试的方法,Div在本例中是 Act。
  • 断言,我们检查结果是否符合预期,在本例中,即如果结果10 / 2应等于5

测试多个输入

在上面的例子中,我们创建了一个测试,并使用一组输入数据运行了它。但是,如何使用另一组输入数据再次运行测试呢?.NET 测试框架提供了一种无需重写整个测试即可实现此目的的方法。

使用 xUnit 的示例

[Theory]
[InlineData(10, 2, 5)]     // Test case: 10 / 2 = 5
[InlineData(20, 4, 5)]     // Test case: 20 / 4 = 5
[InlineData(0, 1, 0)]      // Test case: 0 / 1 = 0
[InlineData(-10, -2, 5)]   // Test case: -10 / -2 = 5
public void Div_ReturnsCorrectResult(
    int numerator,
    int denominator,
    int expected)
    {
        // Arrange
        Calculator calculator = new Calculator();

        // Act
        int result = calculator.Div(numerator, denominator);

        // Assert
        Assert.Equal(expected, result);
    }
Enter fullscreen mode Exit fullscreen mode

这里我们使用[Theory]`and` 而[InlineData]不是 `,[Fact]用于参数化测试。每个[InlineData]属性都为测试方法提供不同的输入集。

类似地,NUnit我们可以使用该[TestCase]属性运行具有多个输入的测试。

[TestCase(10, 2, 5)]     // Test case: 10 / 2 = 5
[TestCase(20, 4, 5)]     // Test case: 20 / 4 = 5
[TestCase(0, 1, 0)]      // Test case: 0 / 1 = 0
[TestCase(-10, -2, 5)]   // Test case: -10 / -2 = 5
public void Div_ReturnsCorrectResult(
    int numerator,
    int denominator,
    int expected)
    {
        // Arrange
        Calculator calculator = new Calculator();

        // Act
        int result = calculator.Div(numerator, denominator);

        // Assert
        Assert.AreEqual(expected, result);
    }
Enter fullscreen mode Exit fullscreen mode

有效单元测试的技术

  • 测试驱动开发 (TDD)
    可以想象成设计一辆汽车,在开始组装之前,首先要绘制详细的蓝图。在测试驱动开发中,我们会在编写实际代码之前先编写单元测试。这种方法确保我们的代码是按照测试设定的规范构建的,从而产生更可靠、更易于测试的软件。

  • 模拟依赖项
    有时,我们想要测试的方法依赖于外部资源,例如在组装汽车时需要某个尚未到货的特定零件。在这种情况下,我们可以使用mocking模拟功能创建所需组件的模拟版本。这样,我们就可以在不依赖实际的、不可用的零件的情况下测试汽车的组装过程。

    例如,如果一个方法从数据库中检索数据,我们可以“模拟”数据库以返回特定值,而无需实际查询真正的数据库。

    在 .NET 中, MoqNSubstitute等库是常用的模拟工具。

  • 测试快乐路径和悲伤路径
    在测试我们的代码时,不仅要检查理想情况(快乐路径),还要检查我们的代码在不太理想的条件下(悲伤路径)的行为。

    • 正常路径测试:这就像确保你按步骤操作时,所有部件都能完美契合。我们总是希望验证代码在一切正常的情况下是否按预期运行。例如,如果我们的方法用于计算两个数的除法,我们会编写一个测试来确保它返回正确的结果。
    • 错误路径测试:在我们的示例中,我们创建了一个测试方法来检查该Div方法,并假设输入始终有效。但是,如果分母为零会发生什么?错误路径测试确保我们的代码在这种情况下不会崩溃,并且能够优雅地处理错误或提供有意义的错误消息。

    以下是一个测试示例:

    [Fact]
    public void Div_DivideByZero_ThrowsDivideByZeroException()
    {
        // Arrange
        Calculator calculator = new Calculator();
    
        // Act & Assert
        Assert.Throws<DivideByZeroException>(() => calculator.Div(10, 0));
    }
    

    对两条路径进行测试可确保我们的代码健壮,能够处理用户可能不会总是遵循“正常路径”的现实世界场景。

  • 编写简洁易维护的测试

    • 保持测试简洁:每个测试都应该只关注一件事。可以把它想象成一次测试一个汽车零部件。
    • 清晰地命名测试:使用描述性的名称,以便任何阅读测试的人都能轻松理解其作用。例如,`is_check_div_method_return_true`Div_ReturnsCorrectResult清楚地表明该测试检查 `Div` 方法是否返回正确的结果。
    • 避免重复代码:如果多个测试需要相同的设置,请考虑使用辅助方法或测试设置方法,以避免重复代码。

结论

.NET 中的单元测试就像搭建乐高积木,需要一块一块地拼装,确保每一块都完美契合后才能进行下一块。通过及早发现 bug、简化维护并增强代码的可靠性,单元测试成为我们开发过程中不可或缺的工具。

务必同时测试正常路径和异常路径。正常路径测试用于确认应用程序在理想条件下运行良好,而异常路径测试则用于检验其应对意外情况的鲁棒性和能力。两者结合,可确保应用程序稳健、安全,并能投入实际应用。

从小处着手,利用 .NET 中提供的工具,例如 `td.js`xUnit或 ` NUnittd.js`,并融入最佳实践,例如测试驱动开发 (TDD)、模拟和全面的路径测试。有了这些技巧,你就能构建出强大可靠、经得起时间考验的应用程序——就像精心搭建的乐高杰作一样😃

如果您喜欢我的文章并感兴趣,欢迎分享。如果您想了解更多关于单元测试的内容,可以访问此链接

祝您编程愉快!🚀💻

文章来源:https://dev.to/tkarropoulos/unit-testing-in-net-tools-and-techniques-nei