次の方法で共有


WMI データ ソース

このセクションに進む前に、TAEF の基本的な実行と、それを使用してテストを作成する方法を理解していることを確認してください。

背景

"WMI" は "Windows Management Instrumentation" の略です。 システムを表現するための業界標準である Common Information Model (CIM) を使用し、 Windows Management Instrumentation は、統一された形でシステム管理情報にアクセスする手段を提供しています。

テストにどのように役立ちますか?

TAEF の WMI DataSource として使用可能な WMI クエリ サポートを使用すると、テストを実行する前に、テストに前提条件を追加したり、テスト マシン上のリソースに関する情報を取得できます。 WMI を使用して実行できるクエリの種類の例を次に示します。

  • テストを実行しようとしているマシンがノート PC であるかどうかを確認し、ノート PC である場合にのみテストを実行する。
  • 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:" で始まっています。これによって、そのデータ ソースが確かに WMI クエリの結果に依存するものであることが TAEF で認識され、データ ドリブン テストと区別されます。 なお、現在 TAEF では、データ ドリブン テストと WMI クエリ結果に依存するテストの両方としてテストを作成することはできません。

では、求める結果を得るための WQL クエリをどのように記述すればよいのでしょうか? WQL クエリ構文は、簡略化された SQL クエリによく似ています。 「スクリプトとアプリケーションの WMI タスク」には、優れたクエリ例が紹介されています。 次に例をいくつか示します。

SELECT Description, DesktopInteract, ProcessId FROM Win32_Service WHERE Name='Themes'
テストで使用する Description、DesktopInteract、ProcessId プロパティを見つけた後、"Themes" サービスでテストを実行します。

SELECT Capabilities, CapabilityDescriptions FROM Win32_Printe
このコンピューターに接続されているプリンターごとにテストを実行します。 テストで各プリンターの Capabilities と CapabilityDescription にアクセスできるようにします。

SELECT Name, User, Location FROM Win32_StartupCommand
Windows の起動時に実行される各プロセスのテストを実行します。 各プロセスについて、プロセスの名前 (Name)、プロセスの場所 (Location)、プロセスを実行するユーザー (User) をテストに通知します。

その他の例については、上記のドキュメントのほか、開いた例に含まれる .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 はバックグラウンドでクエリを実行し、結果を確認します。 クエリの結果として 1 つ以上のオブジェクトが返されると、返されたオブジェクトごとにテストが実行されます。 WQL クエリからオブジェクトが返されない場合、テストはこの情報と共にブロック済みとして記録され、実行は次のテストに進みます。

テストを作成する前に、クエリの確認や検証を行うことをお勧めします。プロセスは非常に簡単です。

  • 実行ダイアログまたはコマンド プロンプトから "wbemtest.exe" を呼び出します
  • 右上の [接続] ボタンをクリックします。
  • 名前空間が "root\cimv2" であることを確認してから、右上の [接続] をもう一度クリックします。
  • [IWbemServices] の [クエリ] をクリックします
  • 表示された編集ボックスにクエリを入力し、[適用] をクリックします

注: "IWbemService" には、クエリに役立つオプションが他にも存在します。 たとえば、"Enum Classes" を使用し、ラジオ ボタンを "recursive" に変更すると、システム上のすべての 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 行目こそが、マネージド データ ドリブン テストに必要になる箇所です。 このコードでは、private の TestContext プロパティを定義し、TAEF が適切な値を設定できるように、public の getter および setter を指定しています。 private の 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" になる可能性もあると思われる場合は、検証または使用の前に確認してください。

たとえば、マネージド テスト コードでは、null 値が DBNull 型のオブジェクトとして TestContext に格納されます。 結果の値を必要な型にキャストする前に、オブジェクトの型が 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    }