玩转浏览器自动化(8)Playwright 使用 NUnit 编写测试

在上一章中,我们编写了一个简单的测试,现在我们需要更深入地了解 NUnit 测试代码的细节,包括测试代码的结构和组成,NUnit 断言的不同形式等等。

测试代码的结构

让我们回顾一下上一篇中的测试代码,它展示了我们需要的最小结构:.

using Microsoft.Playwright;

namespace TestProject1
{
[TestFixture]
public class Tests
{
[Test]
public async Task NavigateToBaidu_TitleIsIsCorrect()
{
var playwright = await Playwright.CreateAsync();

        await using var browser = await playwright.Chromium.LaunchAsync();  

        var page = await browser.NewPageAsync();  

        await page.GotoAsync("https://www.baidu.com");  

        var title = await page.InnerTextAsync("title");  

        Assert.That(title, Is.EqualTo("百度一下,你就知道"));  
    }  
}  
}

首先,每个包含测试的类都必须使用 [TestFixture] 特性进行声明。类必须声明为 public,并且必须有一个公共的、无参数的构造函数,否则 NUnit 无法识别测试类。通常情况下,我们不需要手动添加构造函数,因为默认的隐式构造函数已经满足了这个要求。

接下来,我们需要声明一个或多个测试方法。任何使用 [Test] 特性声明的公共、无参数的方法都将作为测试方法被 NUnit 自动运行。测试方法必须返回 void 或 Task,因为 Playwright 测试方法都是异步的,所以我们必须使用 async 修饰符。我们也可以在测试中包含其他辅助方法,只需不将它们标记为测试方法即可。

测试方法的命名没有任何限制,但我们建议使用有意义的名称,比如 NavigateToBaidu_TitleIsIsCorrect。这样做的好处是,当测试失败时,我们可以很容易地找到它所对应的测试方法。描述性的方法名称可以帮助测试者快速识别测试用例的用途。名称应简洁、明确且一致,我们建议使用以下命名规则:

  • 使用动词开头,比如 Navigate、Click、Fill 等等。
  • 描述测试用例的目的,比如 NavigateToBaidu。
  • 描述测试用例的期望结果,比如 TitleIsCorrect。

最后,在每个测试方法中,我们需要编写测试代码来验证我们的程序是否按照预期运行。通常,测试代码遵循一个标准的公式,称为 Arrange-Act-Assert(AAA)模式:

  • 准备(Arrange):设置测试所需的所有条件,包括创建必要的对象和分配必要的资源等。
  • 执行(Act):调用要测试的方法。
  • 断言(Assert):验证是否得到了预期的结果。

AAA模式将测试代码分为三个明确的部分,每个部分都有特定的目的。通过遵循这种结构,测试代码更加简洁、易于编写和管理,也更容易阅读和理解。测试代码不仅能够证明功能有效,还能演示功能或组件的工作原理,这样无需文档,也能清楚地说明功能的工作原理。此外,AAA模式也有助于调试,因为每个测试都是一个隔离的单元,当测试失败时,可以更容易地定位问题所在。

例如,在上一章中,我们可以将AAA模式应用到测试代码中:

[Test]
public async Task NavigateToBaidu_TitleIsIsCorrect()
{
// 准备
var playwright = await Playwright.CreateAsync();
await using var browser = await playwright.Chromium.LaunchAsync();
var page = await browser.NewPageAsync();

// 执行  
await page.GotoAsync("https://www.baidu.com");  

// 断言  
var title = await page.InnerTextAsync("title");  
Assert.That(title, Is.EqualTo("百度一下,你就知道"));  
}

通过遵循AAA模式,我们可以更加清晰地分离测试的不同部分,并使测试代码更加易于阅读、理解和维护。

断言

在使用 NUnit 进行测试时,我们需要使用断言来验证测试结果是否符合预期。断言是一种验证测试结果的方法,可以是简单的比较,也可以是复杂的逻辑。如果断言失败,测试也将失败。

NUnit 提供了一组断言方法,用于比较预期和实际结果。其中,推荐使用以下断言方法:

Assert.That<TActual>(TActual actual, IResolveConstraint expression)  

该方法接受两个参数,第一个参数是实际的结果,第二个参数是一个断言表达式。断言表达式可以是一个简单的比较,也可以是一个复杂的逻辑表达式。以下是一些常用的断言表达式:

  • Is.EqualTo(expected):判断实际结果是否等于预期结果,例如 Assert.That(1 + 2, Is.EqualTo(3))。可以在任何 Is 断言表达式中间添加 Not,以表示原始表达式的否定,例如 Assert.That(1 + 2, Is.Not.EqualTo(4))。

  • Is.AtMost(expected):判断实际结果是否小于或等于预期结果,例如 Assert.That(1 + 2, Is.AtMost(4))。

  • Is.AtLeast(expected):判断实际结果是否大于或等于预期结果,例如 Assert.That(1 + 2, Is.AtLeast(3))。

  • Is.Null:判断实际结果是否为 null,例如 Assert.That(anObject, Is.Null)。

  • Is.Empty:判断实际结果是否为空集合或字符串,例如 Assert.That(aString, Is.Empty)。

Setup/TearDown

使用 Playwright for NET 进行单元测试时,除了 Arrange-Act-Assert 这个标准公式,还需要进行事后清理。因为每个测试都应该是相互独立的,这样才能在任何时间以任何顺序运行任何单个测试。为了实现这个目标,你可能需要在测试之前重置测试环境,或在测试之后进行清理工作,以确保测试之间的环境隔离。比如,为每个测试创建一个新的浏览器对象,或在测试之后关闭浏览器对象。

为了实现这个目标,你可以使用 [SetUp] 特性来标识一个方法,在每个测试方法运行之前运行。你还可以使用 [TearDown] 特性来标识一个方法,在每个测试方法运行之后运行。通过这种方式,你可以在测试之前进行初始化工作,以及在测试之后进行清理工作。以下是一个示例:

[SetUp]
public void Setup()
{
// 进行初始化工作
}

[Test]
public void TestMethod1()
{
// 执行测试方法 1
}

[Test]
public void TestMethod2()
{
// 执行测试方法 2
}

[TearDown]
public void TearDown()
{
// 进行清理工作
}

在上面的示例中,每次运行测试时,都会先调用 Setup 方法进行初始化工作,然后执行测试方法 1 或测试方法 2,最后再调用 TearDown 方法进行清理工作。这样就可以确保每个测试都是相互独立的,并且可以在任何时间以任何顺序运行任何单个测试。

如何运行测试?

在软件测试中,我们可能需要运行所有的测试,也可能只需运行某个测试类或某个测试方法。在上一章中,我们已经介绍了如何使用命令行运行测试。在这一章中,我们将介绍如何使用 Visual Studio 运行测试。

玩转浏览器自动化(8)Playwright 使用 NUnit 编写测试

在 Visual Studio 中,可以使用测试资源管理器运行测试。测试资源管理器是一个新的 Visual Studio 窗口,它显示了解决方案中的所有测试。可以在 Visual Studio 的菜单栏中找到测试资源管理器,选择“测试”->“测试资源管理器”。

在测试资源管理器中,只需点击窗口上方的“运行”按钮,就可以运行所有测试。当测试执行完毕后,可以看到每个测试的执行时间,还会显示测试的执行结果。如果测试执行成功,那么测试将显示绿色的对勾。如果测试执行失败,那么测试将显示红色的叉。如果测试执行失败,可以通过单击测试来查看失败的原因。

如果要运行单个测试,可以选择测试资源管理器中的测试(也可以选择多个测试),然后单击鼠标右键,在弹出菜单中单击“运行”。

如何过滤测试?

如果需要选择满足特定条件的测试,例如只运行“张三”编写的测试方法,这时我们就需要过滤测试。

你可以将一个或多个 [Category] 特性和不同的测试方法相关联,然后在运行测试时选择要包括哪些类别。这个特性可以帮助你更加灵活地管理和运行测试。

举个例子,假设有一些测试方法只需要几秒钟就能运行,但有一些测试方法要花很长时间运行。我们可以使用类别名称“Short”和“Long”来标识它们,让测试更加高效。你也可以考虑再设置一个类别为“Night”,以标明那些需要在晚上运行的测试。在测试资源管理器中,你可以通过点击窗口上方的“分组依据”按钮,选择“特征”,然后可以看到所有测试方法按类别进行了分组。然后,你可以选择要运行的类别。这个特性可以帮助你更加方便地管理测试。

玩转浏览器自动化(8)Playwright 使用 NUnit 编写测试

但是有时候你需要运行同时满足多个类别的测试方法,就需要使用测试资源管理器右边的“搜索筛选器”。例如,如果要运行同时满足“Short”和“Night”类别的测试方法,那么就需要在“搜索筛选器”中输入“Trait:"Short" Trait:"Night"”。当然,你也可以在“搜索筛选器”中输入其他条件进行过滤,例如,你可以输入“FullName:"Method2"”来筛选出所有名称中包含“Method2”的测试方法。这个特性可以帮助你更加准确地运行测试。

如果你是使用命令行运行测试,可以使用“--filter”参数来指定过滤测试。例如,要运行同时满足“Short”和“Night”类别的测试方法,可以使用下面的命令:

dotnet test --filter "Category = Short & Category = Night"  

总结

为了更好地进行测试,我们需要学习 NUnit 框架的基本使用方法。在本章中,我们已经了解了 NUnit 框架的基本结构和常用的断言方法。

在下一章中,我们将进一步学习如何使用 Playwright 的测试基类来编写 Playwright 测试,这将让我们的测试更加高效和简便。