Unit testing

应测试跨平台应用,因为它们将在现实世界中使用,以提高其质量、可靠性和性能。 应在应用上执行许多类型的测试,包括单元测试、集成测试和用户界面测试。 单元测试是最常见的形式,对于构建高质量应用至关重要。

单元测试采用应用的小单元(通常是方法),将其与代码的其余部分隔离,并验证其行为是否按预期方式。 其目标是检查每个功能单元是否按预期执行,以便错误不会在整个应用中传播。 在发生 bug 的位置检测 bug,比在次要故障点位置间接观察 bug 造成的影响更为有效。

单元测试通常应使用 arrange-act-assert 模式:

Step Description
Arrange 初始化对象并设置传递给受测方法的数据的值。
Act 使用所需参数调用受测方法。
Assert 验证所测试方法的作是否按预期方式运行。

此模式可确保单元测试可读、自描述和一致。

单元测试是软件开发工作流不可或缺的一部分时,对代码质量的影响最大。 单元测试可以充当应用的设计文档和功能规范。 一旦编写了方法,就应该编写单元测试来验证该方法响应标准、边界和不正确输入数据情况的行为,并检查代码做出的任何显式或隐式假设。 或者,如果使用测试驱动开发,则会在代码之前编写单元测试。

Important

单元测试对回归非常有效。 也就是说,功能曾经正常工作,但被错误的更新所干扰。

对于 .NET MAUI 应用,建议使用 xUnit 测试框架。

将 xUnit 测试添加到 .NET MAUI 解决方案

若要将 xUnit 测试添加到 .NET MAUI 解决方案,请执行以下作之一:

xUnit 测试项目的项目文件 (.csproj) 将类似于以下示例:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>net9.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>

    <IsPackable>false</IsPackable>
    <IsTestProject>true</IsTestProject>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="coverlet.collector" Version="6.0.2" />
    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
    <PackageReference Include="xunit" Version="2.9.2" />
    <PackageReference Include="xunit.runner.visualstudio" Version="2.8.2" />
  </ItemGroup>

  <ItemGroup>
    <Using Include="Xunit" />
  </ItemGroup>

</Project>

生成 $(TargetFramework) 属性指定测试项目的目标框架。 这是计算机上安装的最新版本的 .NET。

xunit 包包含测试框架本身的子包,以及检测单元测试常见问题的 Roslyn 分析器。 xunit.runner.visualstudio在 Visual Studio 和dotnet test命令中运行单元测试需要和Microsoft.NET.Test.Sdk包。 包 coverlet.collector 允许收集代码覆盖率。 如果不打算收集代码覆盖率,可以删除此包引用。 有关单元测试的代码覆盖率的详细信息,请参阅 使用代码覆盖率进行单元测试

有两种用于构建应用进行单元测试的主要方法:

  1. 单元测试的代码位于 .NET MAUI 类库项目中。
  2. 单元测试的代码位于 .NET MAUI 应用项目中。

每个方法都需要特定的配置。

配置用于单元测试的 .NET MAUI 类库项目

使用此方法时,要进行单元测试的代码位于 .NET MAUI 类库项目中,该项目由 .NET MAUI 应用项目使用。 若要针对 .NET MAUI 类库编写单元测试,需要更新项目使用的目标框架。 这可以通过将 xUnit 测试项目文件 (.csproj) 中的生成属性的值$(TargetFramework)添加到 $(TargetFrameworks) .NET MAUI 类库项目文件中的生成属性来实现:

<TargetFrameworks>net9.0;net9.0-android;net9.0-ios;net9.0-maccatalyst</TargetFrameworks>

在此示例中,已将值 net9.0 添加到 $(TargetFrameworks) .NET MAUI 类库项目文件中的生成属性。

然后,必须从 xUnit 测试项目添加对 .NET MAUI 类库项目的引用。

配置用于单元测试的 .NET MAUI 应用项目

使用此方法时,要进行单元测试的代码位于 .NET MAUI 应用项目中。 若要针对 .NET MAUI 应用项目编写单元测试,需要更新项目使用的目标框架。 这可以通过将 xUnit 测试项目文件 (.csproj) 中的生成属性的值$(TargetFramework)添加到 $(TargetFrameworks) .NET MAUI 应用项目文件中的生成属性来实现:

<TargetFrameworks>net9.0;net9.0-android;net9.0-ios;net9.0-maccatalyst</TargetFrameworks>

在此示例中,已将值 net9.0 添加到 $(TargetFrameworks) .NET MAUI 应用项目文件中的生成属性。

还需要修改 .NET MAUI 应用项目,以便它不会为 xUnit 测试项目使用的目标框架输出可执行文件。 这可以通过将条件添加到 $(OutputType) .NET MAUI 应用项目文件中的生成属性来实现:

<OutputType Condition="'$(TargetFramework)' != 'net9.0'">Exe</OutputType>

在此示例中,.NET MAUI 应用项目仅在目标框架不是 net9.0时生成可执行文件。

然后,必须从 xUnit 测试项目添加对 .NET MAUI 应用项目的引用。

编写单元测试

xUnit 支持两种类型的单元测试:

Testing type Attribute Description
Facts Fact 始终为 true 的测试,用于测试固定条件。
Theories Theory 只对特定数据集成立的测试。

单元测试应放置在 xUnit 测试项目中,并使用或[Theory]属性进行[Fact]修饰。 以下示例演示使用该属性的 [Fact] 单元测试:

namespace MyUnitTests
{
    public class MyTests
    {
        [Fact]
        public void PassingTest()
        {
            Assert.AreEqual(4, 2+2);
        }

        [Fact]
        public void FailingTest()
        {
            Assert.AreEqual(5, 2+2);
        }
    }
}

在此示例中,测试表示有意通过和失败的测试。

以下示例演示使用该属性的 [Theory] 单元测试:

namespace MyUnitTests
{
    public class MyTests
    {
        [Theory]
        [InlineData(3)]
        [InlineData(4)]
        [InlineData(5)]
        public void MyTheoryTest(int value)
        {
            Assert.True(value % 2 == 1);
        }
    }
}

在此示例中,尽管只有一个测试方法,但实际上有三个测试,因为理论将针对每个数据项运行一次。

Tip

使用每个单元测试测试一个作。 随着测试复杂性的扩大,测试的验证变得更加困难。 通过将单元测试限制为单个问题,可以确保测试可重复、隔离且执行时间较短。 有关详细信息,请参阅 单元测试最佳做法

运行单元测试

单元测试可以在 Visual Studio 的测试资源管理器中运行,也可以使用命令运行 dotnet test 。 有关测试资源管理器的信息,请参阅 使用测试资源管理器运行单元测试。 有关命令的信息dotnet test,请参阅使用 dotnet test 和 xUnit 和 dotnet 测试在 .NET 中的单元测试 C#

使用设备运行程序运行单元测试

还可以使用设备运行程序在设备上运行单元测试。 设备运行程序是一个测试运行程序应用,它提供可视化运行程序 shell 和一些挂钩,以使用 XHarness 从 CLI 运行。 有关详细信息,请参阅 测试设备运行程序 Wiki 中的文档。

See also