wip-3.2

2026-06-05 ⏳1.4分钟(0.6千字)

3.2 Exploring the xUnit testing framework

3.2 探索 xUnit 测试框架

In this section, I give a brief overview of unit testing tools available in .NET, and their features. I’m using xUnit as the unit testing framework (note that you need to install the xunit.runner.visualstudio NuGet package in order to run xUnit tests from Visual Studio). Although this framework works in .NET only, every object-oriented language has unit testing frameworks, and all those frameworks look quite similar to each other. If you’ve worked with one of them, you won’t have any issues working with another.

本节会简要概览 .NET 中可用的单元测试工具及其功能。我使用 xUnit 作为单元测试框架(注意,为了从 Visual Studio 运行 xUnit 测试,你需要安装 xunit.runner.visualstudio NuGet 包)。虽然这个框架只适用于 .NET,但每种面向对象语言都有单元测试框架,而且这些框架彼此看起来都很相似。如果你用过其中一个,使用另一个也不会有问题。

In .NET alone, there are several alternatives to choose from, such as NUnit and the built-in Microsoft MSTest. I personally prefer xUnit for the reasons I’ll describe shortly, but you can also use NUnit; these two frameworks are pretty much on par in terms of functionality. I don’t recommend MSTest, though; it doesn’t provide the same level of flexibility as xUnit and NUnit. And don’t take my word for it—even people inside Microsoft refrain from using MSTest. For example, the ASP.NET Core team uses xUnit.

仅在 .NET 中,就有几个替代选择,例如 NUnit 和内置的 Microsoft MSTest。我个人偏好 xUnit,原因稍后说明;但你也可以使用 NUnit,这两个框架在功能上基本相当。不过我不推荐 MSTest;它没有提供与 xUnit 和 NUnit 相同级别的灵活性。你也不必只听我这么说——甚至 Microsoft 内部的人也避免使用 MSTest。例如,ASP.NET Core 团队使用 xUnit。

I prefer xUnit because it’s a cleaner, more concise version of NUnit. For example, you may have noticed that in the tests I’ve brought up so far, there are no framework-related attributes other than [Fact], which marks the method as a unit test so the unit testing framework knows to run it. There are no [TestFixture] attributes; any public class can contain a unit test. There’s also no [SetUp] or [TearDown]. If you need to share configuration logic between tests, you can put it inside the constructor. And if you need to clean something up, you can implement the IDisposable interface, as shown in this listing.

我偏好 xUnit,是因为它是一个更干净、更简洁的 NUnit 版本。例如,你可能已经注意到,在目前展示的测试中,除了 [Fact] 之外没有其他框架相关特性;[Fact] 用来把方法标记为单元测试,让单元测试框架知道要运行它。没有 [TestFixture] 特性;任何公共类都可以包含单元测试。也没有 [SetUp][TearDown]。如果你需要在测试之间共享配置逻辑,可以把它放在构造函数中。如果需要清理某些内容,可以实现 IDisposable 接口,如下面清单所示。

Listing 3.6

As you can see, the xUnit authors took significant steps toward simplifying the framework. A lot of notions that previously required additional configuration (like [TestFixture] or [SetUp] attributes) now rely on conventions or built-in language constructs.

如你所见,xUnit 作者在简化框架方面做了大量工作。许多过去需要额外配置的概念(例如 [TestFixture][SetUp] 特性),现在依赖约定或语言内置结构。

I particularly like the [Fact] attribute, specifically because it’s called Fact and not Test. It emphasizes the rule of thumb I mentioned in the previous chapter: each test should tell a story. This story is an individual, atomic scenario or fact about the problem domain, and the passing test is a proof that this scenario or fact holds true. If the test fails, it means either the story is no longer valid and you need to rewrite it, or the system itself has to be fixed.

我特别喜欢 [Fact] 特性,尤其是因为它叫 Fact,而不是 Test。它强调了我在上一章提到的经验法则:每个测试都应该讲述一个故事。这个故事是关于问题领域的一个独立、原子的场景或事实,而通过的测试证明这个场景或事实成立。如果测试失败,就意味着这个故事不再有效,需要重写;或者系统本身需要修复。

I encourage you to adopt this way of thinking when you write unit tests. Your tests shouldn’t be a dull enumeration of what the production code does. Rather, they should provide a higher-level description of the application’s behavior. Ideally, this description should be meaningful not just to programmers but also to business people.

我鼓励你在编写单元测试时采用这种思考方式。你的测试不应该只是对生产代码所做事情的枯燥枚举。相反,它们应该提供对应用行为的更高层描述。理想情况下,这种描述不仅对程序员有意义,也应该对业务人员有意义。