泛型方法的单元测试

可以生成泛型方法的单元测试,这与针对其他方法的操作相同,如如何:创建和运行单元测试所述。 下面的部分提供了创建泛型方法的单元测试的相关信息和示例。

类型参数和类型约束

当 Visual Studio 为泛型类(如 MyList<T>)生成单元测试时,将生成两个方法:一个泛型 Helper 和一个测试方法。 如果 MyList<T> 有一个或多个类型约束,则类型参数必须符合所有类型约束。 为了确保泛型测试代码对于所有允许的输入都按预期方式运行,测试方法将使用要测试的所有约束调用泛型帮助器方法。

示例

下面的示例演示了泛型的单元测试:

  • 编辑生成的测试代码。 本示例有两个部分,即“生成的测试代码”和“编辑的测试代码”。 此示例演示如何将通过泛型方法生成的原始测试代码编辑为有用的测试方法。

  • 使用类型约束。 此示例演示使用类型约束的泛型方法的单元测试。 在本示例中,未符合类型约束。

示例 1:编辑生成的测试代码

该部分中的测试代码对名为 SizeOfLinkedList() 的测试代码方法进行了测试。 此方法将返回一个整数,该整数指定了链接列表中的节点数。

“生成的测试代码”部分中的第一个代码示例演示由 Visual Studio 高级专业版 或 Visual Studio 旗舰版 生成的未编辑的测试代码。 “编辑的测试代码”部分中的第二个示例演示如何针对两种不同的数据类型(int 和 char)测试 SizeOfLinkedList 方法的运行状况。

此代码演示两种方法:

  • 测试帮助器方法 SizeOfLinkedListTestHelper<T>()。 默认情况下,测试帮助器方法的名称中包含 TestHelper。

  • 测试方法 SizeOfLinkedListTest()。 每种测试方法都标记有 TestMethod 特性。

生成的测试代码

下面的测试代码是通过 SizeOfLinkedList() 方法生成的。 由于这是未编辑的生成的测试,因此,必须对该测试进行修改以正确测试 SizeOfLinkedList 方法。

public void SizeOfLinkedListTestHelper<T>()
{
    T val = default(T); // TODO: Initialize to an appropriate value
    MyLinkedList<T> target = new MyLinkedList<T>(val); // TODO: Initialize to an appropriate value
    int expected = 0; // TODO: Initialize to an appropriate value
    int actual;
    actual = target.SizeOfLinkedList();
    Assert.AreEqual(expected, actual);
    Assert.Inconclusive("Verify the correctness of this test method.");
}

[TestMethod()]
public void SizeOfLinkedListTest()
{
   SizeOfLinkedListTestHelper<GenericParameterHelper>();
}

在前述代码中,泛型类型参数为 GenericParameterHelper。 虽然您可以编辑此参数以提供特定的数据类型,如下面的示例所示,但也可以在不编辑此语句的情况下运行测试。

编辑的测试代码

在下面的代码中,已对测试方法和测试帮助器方法进行了编辑,使它们成功地对测试代码方法 SizeOfLinkedList() 进行了测试。

测试帮助器方法

测试帮助器方法执行以下步骤,这些步骤对应于标记为步骤 1 至步骤 5 的代码行。

  1. 创建泛型链接列表。

  2. 将四个节点追加到链接列表。 这些节点内容的数据类型未知。

  3. 将链接列表的预期大小分配给变量 expected。

  4. 计算链接列表的实际大小,并将其分配给变量 actual。

  5. 在 Assert 语句中比较 actual 和 expected。 如果实际大小不等于预期大小,则测试失败。

测试方法

将测试方法编译到运行名为 SizeOfLinkedListTest 的测试时调用的代码中。 测试方法执行以下步骤,这些步骤对应于标记为步骤 6 和步骤 7 的代码行。

  1. 调用测试帮助器方法时指定 <int>,以验证该测试对于 integer 变量是否起作用。

  2. 调用测试帮助器方法时指定 <char>,以验证该测试对于 char 变量是否起作用。

public void SizeOfLinkedListTestHelper<T>()
{
    T val = default(T); 
    MyLinkedList<T> target = new MyLinkedList<T>(val); // step 1
    for (int i = 0; i < 4; i++) // step 2
    {
        MyLinkedList<T> newNode = new MyLinkedList<T>(val);
        target.Append(newNode);
    }
    int expected = 5; // step 3
    int actual;
    actual = target.SizeOfLinkedList(); // step 4
    Assert.AreEqual(expected, actual); // step 5
}

[TestMethod()]
public void SizeOfLinkedListTest() 
{
    SizeOfLinkedListTestHelper<int>();  // step 6
    SizeOfLinkedListTestHelper<char>(); // step 7
}

提示

每次运行 SizeOfLinkedListTest 测试时,都会对 TestHelper 方法调用两次。 断言语句必须每次都评估为 true,才能使测试通过。 如果测试失败,可能不清楚是指定 <int> 的调用还是指定 <char> 的调用导致失败。 若要找到答案,可以检查调用堆栈,还可以在测试方法中设置断点,然后在运行测试时进行调试。 有关更多信息,请参见如何:在 ASP.NET 解决方案中运行测试时进行调试

示例 2:使用类型约束

此示例演示使用未符合的类型约束的泛型方法的单元测试。 第一部分演示测试代码项目的代码。 类型约束将突出显示。

第二部分演示测试项目的代码。

测试代码项目

using System;
using System.Linq;
using System.Collections.Generic;
using System.Text;

namespace ClassLibrary2
{
    public class Employee
    {
        public Employee(string s, int i)
        {
        }
    }

    public class GenericList<T> where T : Employee
    {
        private class Node
        {
            private T data;
            public T Data
            {
                get { return data; }
                set { data = value; }
            }
        }
    }
}

测试项目

与新生成的单元测试类似,必须将非没有结论的 Assert 语句添加到此单元测试中以返回有用的结果。 不要将这些语句添加到标有 TestMethod 特性的方法中,而应添加到“TestHelper”方法中,该方法在此测试中称为 DataTestHelper<T>()。

在此示例中,泛型类型参数 T 具有约束 where T : Employee。 本测试方法未符合此约束。 因此,DataTest() 方法包含一个 Assert 语句,该语句提示您需要提供对 T 施加的类型约束。 此 Assert 语句的消息如下:("No appropriate type parameter is found to satisfies the type constraint(s) of T. " + "Please call DataTestHelper<T>() with appropriate type parameters.");

换言之,当您从测试方法 DataTest() 中调用 DataTestHelper<T>() 方法时,必须传递一个类型参数 Employee 或从 Employee 中派生的一个类。

using ClassLibrary2;

using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace TestProject1

{
    [TestClass()]
    public class GenericList_NodeTest
    {
    
        public void DataTestHelper<T>()
            where T : Employee
        {
            GenericList_Shadow<T>.Node target = new GenericList_Shadow<T>.Node(); // TODO: Initialize to an appropriate value
            T expected = default(T); // TODO: Initialize to an appropriate value
            T actual;
            target.Data = expected;
            actual = target.Data;
            Assert.AreEqual(expected, actual);
            Assert.Inconclusive("Verify the correctness of this test method.");
        }

        [TestMethod()]
        public void DataTest()
        {
            Assert.Inconclusive("No appropriate type parameter is found to satisfies the type constraint(s) of T. " +
            "Please call DataTestHelper<T>() with appropriate type parameters.");
        }
    }
}

请参见

概念

单元测试分析

使用单元测试验证代码