65.9K
CodeProject 正在变化。 阅读更多。
Home

MsTest 101:使用 MsTest 运行器

starIconstarIconstarIconstarIconstarIcon

5.00/5 (6投票s)

2018年10月19日

CPOL

9分钟阅读

viewsIcon

15877

充分利用您的测试运行器。

引言

本文档是关于测试的入门介绍,特别是 MsTest 的内部工作原理。本文档属于入门级 101,更多详细深入的文章将很快推出,以帮助您充分利用测试运行器。

本文将向您展示如何设置 [TestClass][TestMethods],以便使用 MsTest 启动您的测试工作。Microsoft 的测试运行器随 .NET Framework 和 .NET Core 一起提供。

背景

市面上有许多测试运行器,但本文将重点关注 MsTest。不过,您可以将相同的原理和学习应用到任何其他单元测试运行器,因为它们提供的体验非常相似。

让我们开始吧!

我创建了一个解决方案来支持本文,供大家使用。它目前托管在 GitHub 上,您可以在以下链接找到它。如果您想下载或克隆该解决方案,这将对您阅读本文非常有帮助。源代码可供您使用和保留。如果您有任何问题或建议,请提交一个 pull request 或在本文底部评论。

  • 代码可下载或克隆GitHub

Sean Rand GitHub MsTest

至此,我希望您对看到使用 MsTest 的测试感到兴奋?到目前为止,希望您已经获取了源代码以便跟随本文。您构建的解决方案应该与下图所示类似,包含 9 个测试并成功构建。

注意:要在 Visual Studio 中查看测试资源管理器,请记住它位于菜单结构:测试 -> 窗口 -> 测试资源管理器。

MsTest Example Sean Rand

我们还在等什么……让我们开始测试吧!

测试类与测试方法

[TestClass] 是添加到普通 C# 类文件上的一个特性。MsTest 使用 [TestClass] 特性来查找解决方案中的所有测试,并使其可执行。如果没有 [TestClass] 特性,即使您编写了 1000 个 [TestMethod],测试运行器也永远找不到任何测试,因此无法执行您的测试。我们热爱测试,所以要确保我们的测试能够被看到并运行。

当您在 Visual Studio 中创建单元测试时,测试类特性会添加到类顶部。但是,如果您将一个普通的 C# 类文件添加到解决方案中,并且该类的目的是用于测试,您将需要始终记住添加此特性。

[TestMethod] 是一个必需的特性,用于使方法能够被 MsTest 识别并作为测试执行。每个测试方法都需要添加 [TestMethod] 特性。没有这个特性,编译器只会将其视为普通的 C# 方法,这不是我们想要的。我们希望测试方法可以被测试运行器执行,因此我们添加了这个特性。

如果您打开解决方案,以及如下所示的 _SimpleUnitTest.cs_ 文件,请注意我们的类上有 [TestClass] 特性,并且每个测试方法都使用了 [TestMethod] 装饰。您说很简单……您说您永远不会忘记这样做,但有一天您可能会,就像我们都会一样。如果回归本源,您总能找到问题所在。

我曾经忘记在我的测试类上添加 [TestClass],糟糕!当我构建解决方案时,我期望看到几个数据驱动的测试,结果却只显示了零个,因为我忘记了那个小小的特性。一旦我在类顶部添加了它并重新构建了解决方案,嘿, presto——几百个测试出现了!

最简单的测试

我一直倡导保持简单并从头开始,所以我们在这个 101 文章中最简单的测试叫做,您猜对了,我们第一个要看的测试是

Simplest Test

[TestMethod]
public void Simplest_Test()
{
     const int one = 1;
     Assert.AreEqual(1, one);
}

上面的测试是我能想到的最简单的测试,它检查 1 = 1……如果您在解决方案中运行该测试,它应该会变成绿色,否则我的数学能力就严重有问题。这个测试向我们展示了几件事,以便我们熟悉测试运行器。

  • 我们使用 [TestMethod] 作为方法的标题,MsTest 可以将其读取为测试。
  • 测试方法始终没有返回类型,只有 void。测试方法不需要是 stringintbool 或其他任何类型,因为我们不从 this 方法返回任何内容,而是在方法中 Assert
  • Assert 是 MsTest 中 Microsoft 断言框架的一个关键字。如果需要,可以将其替换为其他断言框架,如 Shouldly。然而,对于这个解决方案,我们将重点关注完整的 MsTest。
  • 这类测试的执行时间为毫秒。

现在您已经看到了有史以来最简单的测试,用于演示特性和断言的工作原理,我们将继续介绍其他几个测试。接下来的测试和部分内容将基于 MsTest 运行器的特性和能力进行扩展,如果您愿意使用它们。

年龄和姓名测试

这是一个创建新用户 "Bob"(25 岁)的测试。该测试创建一个 Person 的新实例,并传入用户的姓名和年龄。然后,测试创建的用户以确保 Age 属性被正确设置。如果 Person 的构造函数没有正确设置,并且存在一个 bug,无论您传入什么,它总是将年龄设置为 100,那么测试就会失败,这是一个好的失败,因为它暴露了一个 bug。

        [TestMethod]
        public void Age_Test()
        {
            var bob = new Person("Bob", 25);
            Assert.AreEqual(25, bob.Age);
        }

下面的姓名测试执行了相同的测试类型,只是检查 Bob 是否确实被命名为 Bob。如果我们一个测试中有多个断言,比如先是年龄断言,然后是姓名断言,如果年龄断言失败,那么姓名断言就不会被执行。我们喜欢这样做——每个测试添加许多断言,因为第一个失败可能会掩盖其他失败。

        [TestMethod]
        public void Name_Test()
        {
            var bob = new Person("Bob", 25);
            Assert.AreEqual("Bob", bob.Name);
        }

数据驱动的行测试

以下测试只编写一次,但会运行多次。在 MsTest 中,有一个可以添加到测试方法上的特性叫做 DataRow。现在,这个 DataRow 是我们传递给测试的参数,并对它们进行数据驱动。如果一个测试方法有 5 个数据行,该测试将执行 5 次,使用测试行中描述的输入。

示例如下,并可在解决方案中执行。它们是数据驱动简单测试的绝佳方式,可以清楚地看到测试的输入和输出。我在测试解决方案中偶尔使用它们,当像 Excel 文档或数据库这样的 DataSource 对于我想要测试的数据来说有点过度时。

        [DataRow("Bob", 25)]
        [DataRow("Bill", 35)]
        [TestMethod]
        public void Data_Row_Age_Test(string name, int age)
        {
            var bob = new Person(name, age);
            Assert.AreEqual(age, bob.Age);
        }

        [DataRow(57)]
        [DataRow(-100)]
        [DataRow(21)]
        [TestMethod]
        public void Data_Row_Number_Test(int number)
        {
            Assert.AreEqual(21, number);
        }

上述测试都通过 DataRow 特性进行了数据驱动。请注意,如果我将一个 string 作为第一个参数 "Bob" 传入,我的方法就需要一个 string 参数,同样,我传入一个 intage,所以我的方法需要该参数来接受 int。这样,您的测试方法就可以接受参数了。这适用于简单的数据结构,我认为您可以去探索一下,看看它是否适合您,您的需求,如果您需要更复杂的数据结构,我们将在另一个主题中讨论。

也许下周的文章将是 MsTest DataSource,我们将讨论数据库、Excel 文档、JSON 文件以及您想要的任何内容!

异常测试

因此,作为开发人员/QA,我们不喜欢异常,对吧?嗯,在某些情况下,我们希望抛出异常,让其他组件来消耗这些异常并为用户处理。在这种情况下,当我们要引发一个异常时,即负面测试,我们希望确保我们的应用程序/程序抛出正确的异常。您可能会问,我们如何断言抛出了一个异常,因为它似乎很难做到?

MsTest 使这一点变得容易,它提供了一个名为 [ExpectedException] 的特性。它的作用是覆盖任何内部断言,并声明测试的断言是期望类型为 X 的异常。现在,如果被测代码没有引发异常,那么测试方法就会失败,这符合预期;如果引发了异常,但类型不正确,那么测试也会失败,这同样符合预期。看看下面的例子,看看这个特性有多么有用。我们都在某个阶段尝试过处理异常,但 MsTest 通过这个特性让我们能够有效地测试想要抛出异常的情况……这确实很棒!

        [TestMethod]
        [ExpectedException(typeof(NotImplementedException))]
        public void Expected_Exception_Test()
        {
            // Do Something Arrange / Act

            //Expect an exception from the Act, no need to throw it like this usually as its a demo.
            throw new NotImplementedException("This is showing an exception can be a valid test result");
        }

        [TestMethod]
        [ExpectedException(typeof(DivideByZeroException))]
        public void Expected_Exception_Failure_Test()
        {
            // Do Something Arrange / Act

            //Expect an exception from the Act, no need to throw it like this usually as its a demo.
            throw new NotImplementedException();
        }

当您在解决方案中运行上面的测试时,请注意第二个测试未能通过,它失败了。原因是它抛出了一个 NotImplementedException(),但是测试期望抛出一个 DivideByZeroException。自己编写这个测试会很混乱,而 MsTest 让我们能够以一种平滑的方式完成它。

超时测试

我们在测试时,每个人对测试速度都有自己的看法。对我来说,意见并不总是基于事实,如果您告诉我“感觉很慢”,我会问“为什么,您怎么量化慢?”如果一个 API 测试需要 10 毫秒或 20 毫秒,算慢吗?一个 Selenium 测试需要 3 秒或 60 秒,算慢吗?这都取决于非功能性需求。

QA 和开发人员都遭受着缺乏足够数据来挑战或说明“实际上它在非功能性需求范围内”的困境。假设我们有一个评分引擎,我们期望它比 100 毫秒更快地返回结果,我们如何告诉我们的测试来计时呢?

添加一个秒表,拿一个秒表在手里,眨一下眼 :-)... 幸运的是,通过我今天将要介绍的 101 项目,您可以在 2 秒……或者 3 秒……或者 4 秒内为您的测试添加 timeout

请注意,MsTest 提供了一个 Timeout 特性,我们可以使用并将其添加到我们的测试方法中,这真的很酷。请看下面的例子,请注意第一个测试有一个 timeout,如果它没有在 1 秒内执行就会失败,而下一次我们将它设置为“实际上您有无限时间”。请注意,测试时间以毫秒为单位,所以如果您想要一个最多 4 秒的测试,否则失败,将 1 改为 4000,因为这是毫秒。

        [TestMethod]
        [Timeout(1)] // Milliseconds
        public void Timeout_Test()
        {
            var bob = new Person("Bob", 25);
            Assert.AreEqual(25, bob.Age);
        }


        [TestMethod]
        [Timeout(TestTimeout.Infinite)]
        public void Timeout_Infinite_Test()
        {
            var bob = new Person("Bob", 25);
            Assert.AreEqual(25, bob.Age);
        }

关注点

希望本文对您有所帮助,并展示了使用 MsTest 设置测试的最初几个步骤。所有原理都与其他测试运行器共享,所以如果您访问它们的网站

  • TestClass
  • TestMethod
  • 我们的第一个测试
  • 使用 Data Rows 进行数据驱动测试
  • 预期异常及如何处理
  • 添加 Timeout,以便测试在运行时间过长时失败。
  • 请注意,您可以在同一个测试中使用 TimeoutExceptionDataRow,您不需要一次只使用一个。这些可以堆叠使用,意味着您拥有一个真正强大的测试运行器和测试框架,可以开始参与进来。

我很乐意听取任何问题或反馈,并感谢所有坚持到这里的人!祝您开发/测试愉快!

© . All rights reserved.