WMI 数据源

在继续本部分之前,请确保熟悉 TAEF 的基本执行并知道如何使用它创作测试。

背景

“WMI”代表“Windows Management Instrumentation”。 使用通用信息模型 (CIM) ,这是表示系统的行业标准。 Windows Management Instrumentation 提供了一种统一的方式来访问系统管理信息。

它如何帮助我的测试?

使用在 TAEF 中作为 WMI DataSource 提供的 WMI 查询支持,可以在运行测试之前向测试添加前置条件,并获取有关测试计算机上的资源的信息。 下面是可以使用 WMI 进行查询类型的一些示例:

  • 检查运行测试的计算机是否为笔记本电脑,仅当它是笔记本电脑时才运行测试。
  • 检查是否已在测试计算机上安装 Service Pack,仅当已安装时,才运行测试。
  • 检索测试计算机上的所有可移动驱动器和本地硬盘驱动器,并为与查询匹配的每个驱动器运行测试。
  • 仅当测试计算机未加入域时运行测试或
  • 仅当测试计算机已加入域并检索域名时,才运行测试。

这有望让你了解在何处以及如何利用 WMI DataSource 进行测试。 让我们看看如何在创作 TAEF 测试时添加此 WMI 查询支持。

使测试成为 WMI DataSource 测试所需的唯一特殊元数据是“DataSource”。 DataSource 语法必须如下所示:

[DataSource("WMI:<WQL query>")]

或在本机代码中:

TEST_METHOD_PROPERTY(L"DataSource", L"WMI:<WQL query>")]

必须注意到 DataSource 值以“WMI:”开头,这让 TAEF 知道,这确实是测试的数据源,它依赖于 WMI 查询结果,并将其与数据驱动的测试区分开来。 这是一个很好的机会,提及目前 TAEF 不支持既是数据驱动的测试,又是依赖于 WMI 查询结果的测试。

下一个问题自然是,如何针对要查找的内容编写 WQL 查询? WQL 查询语法与简化的 SQL 查询非常相似。 脚本 和应用程序的 WMI 任务中提供了一些非常好的查询示例。 以下是一些示例:

SELECT Description, DesktopInteract, ProcessId FROM Win32_Service WHERE Name='Themes'
在找到要在测试中使用的 Description、DesktopInteract 和 ProcessId 属性后,对“主题”服务运行测试。

SELECT 功能,CapabilityDescriptions FROM Win32_Printe
对连接到此计算机的每台打印机运行测试。 允许测试访问每台打印机的功能和功能说明。

SELECT Name、User、Location FROM Win32_StartupCommand
对 Windows 启动时运行的每个进程运行测试。 对于每个进程,让测试知道进程的名称、它的位置 (位置) ,以及进程运行的用户身份。

可以在上述文档以及打开的示例的 .cs 文件和头文件中找到更多示例。 一般过度简化的语法如下:

SELECT <comma separated properties> FROM <WMI Class name> [WHERE <add condition on some properties>]

在刚才看到的示例中,Win32_Service、Win32_Printer和Win32_StartupCommand都是 WMI 类。 可以在 WMI 类中查找 WMI 类

TAEF 不支持检索系统属性。

在后台,TAEF 将为你执行查询并确认结果。 如果查询结果返回了至少一个对象,则会为每个返回的对象执行测试。 如果 WQL 查询未返回任何对象,则测试将记录为使用此信息阻止,然后执行将转到下一个测试。

在创作测试之前检查或验证查询似乎是一个好主意,并且是一个非常简单的过程:

  • 从运行对话框或命令提示符调用“wbemtest.exe”
  • 单击右上角的“连接”按钮。
  • 请确保命名空间为“root\cimv2”,然后再次单击右上角的“连接”。
  • 在“IWbemServices”下,单击“查询”
  • 在出现的编辑框中输入查询,然后单击“应用”

注意:“IWbemService”有几个其他选项可以帮助你进行查询。 例如,使用“枚举类”并将单选按钮更改为“递归”将有助于查看系统上的所有 WMI 类。

检索使用 WMI 查询查询的属性

现在,你已了解如何为测试方法提出 WMI 查询,以及如何在创作测试时将其作为元数据应用。 你还了解如何使用 wbemtest.exe 确认查询是否有效。 现在,让我们看一下如何检索要查找的属性值。

检索此信息的基础知识与检索数据驱动测试的值非常相似。 例如,在托管代码中,如下所示:

1 namespace WEX.Examples
2 {
3     using Microsoft.VisualStudio.TestTools.UnitTesting;
4     using System;
5     using System.Collections;
6     using System.Data;
7     using WEX.Logging.Interop;
8     using WEX.TestExecution;
9
10    [TestClass]
11    public class CSharpWmiDataSourceExample
12    {
13        [TestMethod]
14        [DataSource("WMI:SELECT Description, DesktopInteract, ProcessId FROM Win32_Service WHERE Name='Themes'")]
15        public void ThemesTest()
16        {
17            String description = (String)m_testContext.DataRow["Description"];
18            Boolean desktopInteract = (Boolean)m_testContext.DataRow["DesktopInteract"];
19            UInt32 processId = (UInt32)m_testContext.DataRow["ProcessId"];
20            Log.Comment("Themes service is running on process " + processId.ToString() + " with desktop interact set to "
                           + desktopInteract.ToString());
21            Log.Comment("Themes service description: " + description);
22        }
23        ...
24        public TestContext TestContext
25        {
26            get { return m_testContext; }
27            set { m_testContext = value; }
28        }
29
30        private TestContext m_testContext;
31    }
32}

上面示例中的第 24-30 行正是托管数据驱动测试所需的内容。 你定义了一个专用 TestContext 属性,并为其提供了公共 getter 和 setter,以便 TAEF 设置正确的值。 使用专用 TestContext 属性,可以检索从 TAEF 检索到的任何 WMI 查询结果对象的属性的当前值。

用于检索 WMI 属性的本机代码非常相似。 与本机数据驱动测试一样,你将使用 TestData 获取属性值。 例如,让我们考虑获取默认打印机的属性的测试。 头文件创建此测试,如下所示:

1        // Test on the default printer and its driver name
2        BEGIN_TEST_METHOD(DefaultPrinterTest)
3            TEST_METHOD_PROPERTY(L"DataSource",
              L"WMI:SELECT DriverName, DeviceId, LanguagesSupported FROM Win32_Printer WHERE Default = True")
4        END_TEST_METHOD()

为此,cpp 文件中的检索代码如下所示:

1     void WmiExample::DefaultPrinterTest()
2     {
3         String deviceId;
4         VERIFY_SUCCEEDED(TestData::TryGetValue(L"DeviceId", deviceId));
5
6         String driverName;
7         VERIFY_SUCCEEDED(TestData::TryGetValue(L"DriverName", driverName));
8
9         TestDataArray<unsigned int> languagesSupported;
10        VERIFY_SUCCEEDED(TestData::TryGetValue(L"LanguagesSupported", languagesSupported));
11
12        Log::Comment(L"The default driver is " + deviceId + L" which is a " + driverName);
13        size_t count = languagesSupported.GetSize();
14        for (size_t i = 0; i < count; i++)
15        {
16            Log::Comment(String().Format(L"Language supported: %d", languagesSupported[i]));
17        }
18    }

考虑可能的 NULL 属性值

要记住的部分是,WMI 查询可能并不总是返回非 null 属性。 有时返回的 WMI 属性值可能为“null”。 如果你认为要查找的属性在某些情况下可能为“null”,则在验证或尝试使用它之前,检查它。

例如,在托管测试代码中,TestContext 会将 null 值存储为 DBNull 类型的对象。 在尝试将结果值强制转换为预期的类型之前,必须检查对象是否为 DBNull 类型。 让我们来实际操作一下:

1 namespace WEX.Examples
2 {
3     using Microsoft.VisualStudio.TestTools.UnitTesting;
4     using System;
5     using System.Collections;
6     using System.Data;
7     using WEX.Logging.Interop;
8     using WEX.TestExecution;
9
10    [TestClass]
11    public class CSharpWmiDataSourceExample
12    {
13        [TestMethod]
14        [DataSource("WMI:SELECT MaximumComponentLength, Availability, DeviceId, DriveType, Compressed
                         FROM Win32_LogicalDisk WHERE DriveType=2 Or DriveType=3")]
15        public void LogicalDiskTest()
16        {
17            UInt32 driveType = (UInt32)m_testContext.DataRow["DriveType"];
18            Log.Comment("DeviceId is " + m_testContext.DataRow["DeviceId"]);
19            Log.Comment("DriveType is " + driveType.ToString());
20
21            object nullCheckCompressed = m_testContext.DataRow["Compressed"];
22            Log.Comment("Compressed's type is: " + nullCheckCompressed.GetType().ToString());
23            if (nullCheckCompressed.GetType() == typeof(DBNull))
24            {
25                Log.Comment("Compressed is NULL");
26            }
27            else
28            {
29                Boolean compressed = (Boolean)nullCheckCompressed;
30                Log.Comment("Compressed is " + compressed.ToString());
31            }
32
33            object nullCheckMaxComponentLength = m_testContext.DataRow["MaximumComponentLength"];
34            if (nullCheckMaxComponentLength.GetType() == typeof(DBNull))
35            {
36                Log.Comment("MaxComponentLength is NULL");
37            }
38            else
39            {
40                UInt32 maxComponentLength = (UInt32)nullCheckMaxComponentLength;
41                Log.Comment("MaxComponentLength is " + maxComponentLength.ToString());
42            }
43
44            object nullCheckAvailability = m_testContext.DataRow["Availability"];
45            if (nullCheckAvailability.GetType() == typeof(DBNull))
46            {
47                Log.Comment("Availability is NULL");
48            }
49            else
50            {
51                UInt32 availability = (UInt32)nullCheckAvailability;
52                Log.Comment("Availability is " + availability.ToString());
53            }
54        }
55        ...
56        public TestContext TestContext
57        {
58            get { return m_testContext; }
59            set { m_testContext = value; }
60        }
61
62        private TestContext m_testContext;
63    }
64}

例如,在上述测试中,“Compressed”、“MaximumComponentLength”和“Availability”在某些情况下可为 null, (查询返回可移动驱动器(如软盘驱动器) )。 你希望确保测试在此类情况下的行为正确。 为此,将属性值检索为 对象,如果其类型为“DBNull”,则检查。 如果为 ,则表示返回的属性值为 null。 如果不是,则返回的值不为 null,因此有效 - 因此请将其强制转换为适当的类型,并将其用于测试。

本机检索 API 也是如此 - 返回的属性值可以为 NULL。 这意味着,你需要检查 TestData 是否在不使用验证调用的情况下成功检索了值 (,因为无法检索可能是因为值) 为 null。 例如,你可能有一个依赖于 WMI 查询的测试方法:

1        // Test on only local (drive type = 3) or removable (drive type = 2) harddrive
2        BEGIN_TEST_METHOD(LocalOrRemovableHardDriveTest)
3            TEST_METHOD_PROPERTY(L"DataSource", L"WMI:SELECT DeviceId, DriveType, Availability,
                  MaximumComponentLength FROM Win32_LogicalDisk WHERE DriveType=2 OR DriveType=3")
4        END_TEST_METHOD()

你可能已将“Availability 和”MaximumComponentLength“作为 NULL 值返回。 因此,编写测试以考虑此问题,如下所示:

1     void WmiExample::LocalOrRemovableHardDriveTest()
2     {
3         String deviceId;
4         VERIFY_SUCCEEDED(TestData::TryGetValue(L"DeviceId", deviceId));
5         int driveType;
6         VERIFY_SUCCEEDED(TestData::TryGetValue(L"DriveType", driveType));
7
8         unsigned int maxComponentLength;
9         if (SUCCEEDED(TestData::TryGetValue(L"MaximumComponentLength", maxComponentLength)))
10        {
11            Log::Comment(String().Format(L"MaximumComponentLength: %d", maxComponentLength));
12        }
13
14        unsigned int availability;
15        if (SUCCEEDED(TestData::TryGetValue(L"Availability", availability)))
16        {
17            Log::Comment(String().Format(L"Availability: %d", availability));
18        }
19
20        Log::Comment(L"DeviceId: " + deviceId);
21        Log::Comment(String().Format(L"DriveType: %d", driveType));
22    }