本教程逐步讲解了构建示例解决方案的交互式体验,以学习单元测试概念。 如果想要使用预生成解决方案遵循本教程, 请先查看或下载示例代码 ,然后再开始。 有关下载说明,请参阅 示例和教程。
本文介绍如何测试 .NET Core 项目。 如果要测试 ASP.NET Core 项目,请参阅 ASP.NET Core中的
先决条件
- .NET 8 SDK 或更高版本。
- 你选择的文本编辑器或代码编辑器。
创建源项目
打开 shell 窗口。 创建名为 unit-testing-with-fsharp 的目录来保存解决方案。 在此新目录中,运行以下命令,为类库和测试项目创建新的解决方案文件:
dotnet new sln
接下来,创建 MathService 目录。 以下大纲显示到目前为止的目录和文件结构:
/unit-testing-with-fsharp
unit-testing-with-fsharp.sln
/MathService
将 MathService 设为当前目录并运行以下命令以创建源项目:
dotnet new classlib -lang "F#"
创建数学服务的失败实现:
module MyMath =
let squaresOfOdds xs = raise (System.NotImplementedException("You haven't written a test yet!"))
将目录更改回 unit-testing-with-fsharp 目录。 运行以下命令,将类库项目添加到解决方案:
dotnet sln add .\MathService\MathService.fsproj
创建测试项目
接下来,创建 MathService.Tests 目录。 以下大纲显示了目录结构:
/unit-testing-with-fsharp
unit-testing-with-fsharp.sln
/MathService
Source Files
MathService.fsproj
/MathService.Tests
将 MathService.Tests 目录设为当前目录,并使用以下命令创建新项目:
dotnet new nunit -lang "F#"
此命令创建一个使用 NUnit 作为测试框架的测试项目。 生成的模板在 MathServiceTests.fsproj 中配置测试运行程序:
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />
<PackageReference Include="NUnit" Version="4.1.0" />
<PackageReference Include="NUnit3TestAdapter" Version="4.5.0" />
</ItemGroup>
测试项目需要其他包来创建和运行单元测试。
dotnet new
在上一步骤中添加了 NUnit 和 NUnit 测试适配器。 现在,将 MathService
类库添加为项目的另一个依赖项。 使用 dotnet reference add
命令:
dotnet reference add ../MathService/MathService.fsproj
可以在 GitHub 上的 示例存储库 中看到整个文件。
你有以下最终解决方案布局:
/unit-testing-with-fsharp
unit-testing-with-fsharp.sln
/MathService
Source Files
MathService.fsproj
/MathService.Tests
Test Source Files
MathService.Tests.fsproj
在 unit-testing-with-fsharp 目录中执行以下命令:
dotnet sln add .\MathService.Tests\MathService.Tests.fsproj
创建第一个测试
编写一个失败测试,使其通过,然后重复此过程。 打开 UnitTest1.fs 并添加以下代码:
namespace MathService.Tests
open System
open NUnit.Framework
open MathService
[<TestFixture>]
type TestClass () =
[<Test>]
member this.TestMethodPassing() =
Assert.That(true, Is.True)
[<Test>]
member this.FailEveryTime() = Assert.That(false, Is.True)
该 [<TestFixture>]
属性表示包含测试的类。 该 [<Test>]
属性表示由测试运行程序运行的测试方法。 在 unit-testing-with-fsharp 目录中,执行 dotnet test
以生成测试和类库,然后运行测试。 NUnit 测试运行程序包含用于运行测试的程序入口点。
dotnet test
使用已创建的单元测试项目启动测试运行程序。
这两个测试展示了最基本的通过和失败情况。
My test
通过,而 Fail every time
失败。 现在,为 squaresOfOdds
该方法创建一个测试。 该方法 squaresOfOdds
返回属于输入序列的所有奇数整数值的平方序列。 可以迭代创建验证功能的测试,而不是一次性编写所有这些函数。 若要让每个测试都通过,意味着要针对此方法创建必要的功能。
可以写入的最简单测试是使用所有偶数进行调用 squaresOfOdds
,其中结果应为空整数序列。 下面是测试:
[<Test>]
member this.TestEvenSequence() =
let expected = Seq.empty<int>
let actual = MyMath.squaresOfOdds [2; 4; 6; 8; 10]
Assert.That(actual, Is.EqualTo(expected))
请注意,序列 expected
已转换为列表。 NUnit 框架依赖于许多标准 .NET 类型。 该依赖项意味着你的公共接口和预期结果支持 ICollection ,而不是 IEnumerable。
运行测试时,会看到测试失败。 因为尚未创建实现。 通过在 MathService 项目中的 Library.fs 类中编写最简单代码来使此测试通过,该代码有效:
let squaresOfOdds xs =
Seq.empty<int>
在 unit-testing-with-fsharp 目录中,再次运行 dotnet test
。
dotnet test
命令构建 MathService
项目,然后构建 MathService.Tests
项目。 构建这两个项目后,它将运行你的测试。 现在两个测试通过。
完成所有要求
你已经通过了一个测试,现在可以编写更多测试。 下一个简单的事例适用于唯一奇数为 1
的序列。 数字 1 更容易,因为 1 的正方形为 1。 下面是下一个测试:
[<Test>]
member public this.TestOnesAndEvens() =
let expected = [1; 1; 1; 1]
let actual = MyMath.squaresOfOdds [2; 1; 4; 1; 6; 1; 8; 1; 10]
Assert.That(actual, Is.EqualTo(expected))
执行 dotnet test
时,新测试失败。 必须更新 squaresOfOdds
该方法才能处理此新测试。 必须筛选序列中的所有偶数,以使此测试通过。 为此,可以编写一个小型筛选器函数并使用 Seq.filter
:
let private isOdd x = x % 2 <> 0
let squaresOfOdds xs =
xs
|> Seq.filter isOdd
还要执行一个步骤:计算每个奇数的平方值。 首先编写新的测试:
[<Test>]
member public this.TestSquaresOfOdds() =
let expected = [1; 9; 25; 49; 81]
let actual = MyMath.squaresOfOdds [1; 2; 3; 4; 5; 6; 7; 8; 9; 10]
Assert.That(actual, Is.EqualTo(expected))
可以通过映射操作传递经过筛选的序列来计算每个奇数的平方,以此方式来修复测试的缺陷:
let private square x = x * x
let private isOdd x = x % 2 <> 0
let squaresOfOdds xs =
xs
|> Seq.filter isOdd
|> Seq.map square
你已为该库生成了一个小库和一组单元测试。 你构建了解决方案,以便添加新的包和测试是正常工作流的一部分。 你大部分时间都专注于解决应用程序的目标。