次の方法で共有


最新のアプリ

最新の Web サイトとアプリにエンドツーエンドのテストを実行する

Rachel Appel

Rachel Appelソフトウェアはかつてないほど複雑になりました。Windows、iOS、Web、Android、モノのインターネット (IoT) デバイス、スマートウォッチなど、どの用途のソフトウェアでも、さまざまな処理を実行するようになっています。したがって、ソフトウェアは正確でなければなりません。つまり、仕様どおりに機能しなければなりません。そのため、ソフトウェアをテストするかどうかは開発者が責任を負っています。

テストを実行しなければ、コードが仕様どおりに機能するかどうか検証するのが難しくなります。また、コードを分離してバグを修正することも難しくなります。テストは重要ですが、テストのためのテストや 100% のカバレッジをうたう権利を得るためのテストではなく、正当な理由があるテストを実行することも重要です。どのような場合でも、小規模で効果的なテストは、コード ベース全体をカバーする無意味なテストに勝ります。この記事では、サーバー側のコードやクライアント側のコードに単体テストを実行する方法に加えて、コード化された UI テスト (CUIT) で UI テストを自動化する方法について説明します。この 3 つの基本的なテスト領域で、ソフトウェアの主な機能をテストします。

単体テストを実行する

単体テストを実行すると、バグを減らせて、テスト対象のコードに関するドキュメントを作成できます。回帰テスト、ユーザー受け入れテスト、UI オートメーションなどの品質保証テストも重要です。予算が限られがちでチームが小規模なために、実行するテストを取捨選択しなければならないこともよくあります。アプリに対してどれほど品質保証テストを行うかについての判断は、開発者が責任を負っています。少なくとも、単体テストと UI テストは高品質の維持に効果があります。

単体テストには、さまざまなオープン ソースや商用のソフトウェア パッケージを利用できます。人気テスト スイートの多くは NuGet パッケージを公開しているため、直接かつ簡単に Visual Studio にダウンロードできます。C# 用の人気テスト フレームワークは xUnit、nUnit、MSTest、TestDriven.NET などで、JavaScript 用のテスト フレームワークは、qUnit、JSUnit、YUI Test などです。

単体テストを既定のテストの種類にすることは多いですが、ソフトウェアのテスト方法は他にもあります。UI オートメーション (CUIT) テスト、ユーザー受け入れテスト、回帰テスト、統合テスト、およびあらゆる種類の専門テストがあります。Wikipedia のページ (http://ja.wikipedia.org/wiki/ソフトウェアテスト) には、ソフトウェアをテストする方法が多数記載されています。

優れた単体テストは、テストあたり 1 つの機能だけをテストします。単一責任の原則 (SRP) は、コードのモジュール性を確保する方法の 1 つです。SRP とは、実装コードであれテスト自体であれ、コードが 1 つの機能だけを対象としていて、その機能を適切に実現していることを意味します。単体テストは、データベースやファイルなどのリソースに直接アクセスしてはなりません。単体テストは、こうしたリソースのシミュレーションであるモック オブジェクトを使用する必要があります。また、他のコードと同様に、単体テストは小規模である必要もあります。

優れたテストを作成する別の方法としては、テスト駆動開発 (TDD) の原則に従うことが挙げられます。TDD とは、一連のテストを中心にコードを開発する手法です。開発者はまず、失敗するテストを作成します。次に、そのテストが成功する正しいコードを記述し、最後にリファクタリングを行います。個人的には、この手法を "レッド、グリーン、リファクタリング" と呼んでいます。この手法を使用すると、一貫した方法でコードを構造化して記述できます。一貫性は、ソフトウェア開発で重んじられる長所です。TDD についてはこの記事で取り扱う範囲を超えていますが、ぜひ皆さんのチームで調べて試してみてください。

この記事では、xUnit と qUnit を使用した単体テストについて説明します。また、CUIT による UI オートメーション テストについても解説します。このようにすることで、開発者が実行する必要がある、特に重要なテストについてよく理解できます。

xUnit でサーバー側の C# をテストする

単体テスト パッケージを Visual Studio に統合するのは感動的なほど簡単です。多くのフレームワークは、NuGet パッケージとして入手できます。たとえば xUnit を使用する場合は、NuGet パッケージ マネージャーを起動し、"xUnit" を検索して、コア パッケージとそのテスト ランナー パッケージの両方について [インストール] をクリックします。xUnit を使用すると、ASP.NET Web サイト、Windows フォーム、Windows Presentation Foundation (WPF)、Windows ストア アプリ、Windows Phone アプリに加えて、Microsoft Intermediate Language (MSIL) にコンパイル可能なあらゆる言語をテストできます。F# さえも xUnit での単体テストが可能です。つまり、マイクロソフトのあらゆる開発テクノロジをテスト可能です。

xUnit を使用するには、クラス ライブラリ プロジェクトをソリューションに追加します。わかりやすいように、一般的な名前付け規則に従って ProjectName.Tests のような名前を付けることをお勧めします。この例では、"ProjectName" がテスト対象プロジェクトの名前です。テストには、テスト対象プロジェクトへの参照を必ず追加します。次に、クラス ファイルに名前を付けてテストに追加し、テスト クラスを記述します。

図 1 は、1 つのテストとそのテスト対象のコードを含む、xUnit テスト クラスのサンプルを示しています。図 1 のコードは、滞在時間に基づいて顧客のステータスを決定します。図 1 のテストは、コードが正しいことを確認します。

図 1 xUnit コードのサンプルに対するテスト

// Unit tests that test customer-related features
public class CustomerTests
{
  [Theory]
  [InlineData(99D)]
  [InlineData(1001D)]
  [InlineData(5001D)]
  [InlineData(10001D)]
  public void VerifyCustomerStatus(double TotalSpent) {
    // Arrange           
    var status = new Status();
    var testStatus=OrderForm.Models.Status.StatusLevel.None;
    // Act
    var currentStatus = status.GetCurrentStatus(TotalSpent);
    // Assert           
    Assert.True(testStatus <= currentStatus);
  }
}
// Logic that sets the customer status
public class Status
{
  public StatusLevel GetCurrentStatus(decimal ForAmount)
  {
    var level = StatusLevel.None;
    var amt = Convert.ToInt32(ForAmount);
    if (amt > 1000 && amt <= 5000) { level = StatusLevel.Silver; }
    else if (amt > 5000 && amt <= 10000) { level = StatusLevel.Gold; }
    else if (amt > 10000) { level = StatusLevel.Platinum; }
    return level;
  }
  public enum StatusLevel
  {
    None = 0,
    Silver = 1,
    Gold = 2,
    Platinum = 3
  }          
}

単体テスト メソッドでは、図 1 にも示しているように、よく使用される Arrange、Act、Assert (AAA) パターンを採用しています。まずテスト条件を準備 (Arrange) し、次にその条件に基づいて実行 (Act) し、最後に出力をアサート (Assert) します。

AAA パターンは何にでも適用できますが、ソフトウェアの品質を上げるには意味のあるテストを実行しなければなりません。多くのテスト フレームワークには、Equal、NotEqual、Same、NotSame、Contains、DoesNotContain、InRange、IsNotEmpty など、大量のアサーションが用意されています。もちろん、正確な名前やメソッドはフレームワークによって異なりますが、各フレームワークではいくつものアサーションから選択できます。

既にお気付きかもしれませんが、今回のテスト クラスを修飾しているのは一般的な [Test] 属性ではなく、[Theory] 属性と [InlineDate] 属性です。テスト メソッドにパラメーターがない場合、xUnit では、[Fact] 属性を使用してメソッドをテスト メソッドとしてマークできます。これら 2 つのデータ注釈を使用することで、図 1 のテスト メソッドのシグネチャに示している TotalSpent パラメーターをテストで受け取れるようになります。メソッドに指定した各 [InlineDate] 属性あたり 1 回のテストが実行され、Visual Studio のテスト エクスプローラーに各テストの結果が表示されます (図 2 参照)。

テスト エクスプローラーに表示されている、4 つのデータ ポイントを使用した VerifyCustomerStatus テスト メソッド
図 2 テスト エクスプローラーに表示されている、4 つのデータ ポイントを使用した VerifyCustomerStatus テスト メソッド

いつでもテスト エクスプローラーの各テストに対するコンテキスト メニューを使用して、実行、デバッグ、分析、プロファイルなどを行うオプションを表示可能です。コードまたはテストを変更した場合は必ず、テストをコンパイルして実行する必要があります。ビルドのたびに自動でテストが実行されるようにオプションを設定するには、Visual Studio の [テスト] メニューで [テスト設定] をポイントし、[ビルド後にテストを実行] をクリックします。

単体テストを使いこなすには、モック オブジェクトを避けては通れません。一般的に、データベースのテストを試みる場合、実際のライブ データを編集することはお勧めしません。したがって、実際のデータベース、Web サービス、ビジネス オブジェクトなどのシミュレーションを行う方法の 1 つとして、モック作成を使用できます。モック作成についてはこの記事で取り扱う範囲を超えているため、モックを作成する段階まで進んだ方は、Moq、Telerik JustMock、または Rhino Mocks を Web で検索してください。どれも人気のモック作成ソフトウェアであり、ドキュメントやサポートが豊富に用意されています。

qUnit でクライアント側の JavaScript をテストする

qUnit は、クライアント側の JavaScript 向けテスト フレームワークです。軽量かつシンプルで、簡単に使用できます。qUnit を使用すると、あらゆる JavaScript または jQuery をテストできます。クライアント側の JavaScript を扱っているので、テストの必要がない UI 操作が発生する場合があります。そこで、リンク ホバー、外観に関する UI の変更などの要素についてはテストをスキップできます。また、UI テストは UI オートメーション テストでもカバーできます。ビジネス ルール、API コード、およびサーバ側の検証については、どれも十分なカバレッジを達成する必要があります。さらに、ロジックと検証を行うすべての JavaScript もテストが必要です。

Visual Studio に直接統合されているコンパイル型言語テストとは異なり、qUnit のテスト ハーネスは開発者が独自にビルドする必要があります。ただし、テスト ハーネスのビルドはきわめて容易で、必要なコードは約 17 行だけです。コピーして使用できるコードを図 3 に示します。

図 3 qUnit の HTML テスト ハーネス

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
  <title>Qunit Unit Testing</title>
  <link href="Content/qunit.css" rel="stylesheet" />
</head>
<body>
  <h1 id="qunit-header">QUnit Test Suite</h1>
  <h2 id="qunit-banner"></h2>
  <div id="qunit-testrunner-toolbar"></div>
  <h2 id="qunit-userAgent"></h2>
  <ol id="qunit-tests"></ol>
  <script src="Scripts/qunit.js"></script>      
  <script src="Scripts/logic.js"></script>
  <script src="Scripts/tests.js"></script>   
</body>
</html>

図 3 を見ると、qunit.css、qunit.js、logic.js、および tests.js の順に参照が並んでいることがわかります。この順番が重要です。qunit.js の後に logic.js を読み込み、logic.js の後に tests.js を読み込まければなりません。logic.js は開発者が独自に作成するコードなので、自由に名前を設定できます。quint.js と tests.js の間で複数の .js ファイルを参照する場合もあります。テスト ハーネス ページの HTML 構造には、いくつかの見出しタグと、個別のテスト結果を表示する番号付きリストが含まれています。これらの見出しやリストのスタイルはすべて、qunit.css ファイルで指定しています。ベスト プラクティスとして、Web ページの最後、つまり </body> 終了タグの直前には JavaScript への参照のみを配置することをお勧めします (図 3 参照)。

qUnit でテストするには、テストを .js ファイル内に配置し、そのファイルに tests.js という名前を付けます。tests.js には、qUnit テスト関数の呼び出しを記述します。テスト関数は、呼び出し先関数の文字列名と、実際にテストを実行する匿名関数を受け取ります。図 4 に示すように、テスト関数に渡した文字列がテスト名としてテスト ランナーに表示されます。パラメーターをテストに渡す代わりに、JavaScript の動的な性質を利用して、問題のコードを複数のアサートで複数回呼び出すこともできます。図 5 は、VerifyCustomerStatus テスト関数の複数のアサート出力を示しています。

図 4 qUnit による JavaScript の getCurrentStatus 関数のテストとロジック自体

// Tests to test customer statuses
test('VerifyCustomerStatus', function () {
  var silver = getCurrentStatus(1001);
  equals(silver, "Silver", "Pass");
  var gold = getCurrentStatus(5001);
  equals(gold, "Gold", "Pass");
  var platinum = getCurrentStatus(10001);
  equals(platinum, "Platinum", "Pass");
})
// Logic to set customer statuses
function getCurrentStatus(amt) {
  if (amt > 1000 && amt <= 5000) { return "Silver"; }
  else if (amt > 5000 && amt <= 10000) { return "Gold"; }
  else if (amt > 10000) { return "Platinum"; }
  return "None";
}

ブラウザーに表示された qUnit テストの結果
図 5 ブラウザーに表示された qUnit テストの結果

JavaScript 用 Windows ライブラリ (WinJS) を使用した Windows ストア アプリまたは Windows Phone アプリを記述している場合、心配は無用です。QUnitMetro を bit.ly/1F2RsO7 (英語) からダウンロードできます。このパッケージを利用すると、Web ページでの場合とまったく同じようにアプリ ストア コンテナーで qUnit を実行できます。QUnitMetro の実態は qUnit なので、コーディング方法やテストの記述方法は qUnit とまったく同じです。

CUIT で UI をテストする

ネイティブ UI 用テストを自動化する必要がある場合は、CUIT を使用します。CUIT は、Visual Studio のテスト機能としてはほとんど知られていないようです。Visual Studio で、Web アプリ、Windows ストア アプリ、および Windows Phone アプリ用の CUIT を作成できます。Visual Studio の CUIT では、Windows 上の任意の UI をテストできます。UI をテストするにはソース コードさえ必要ありません。実行中の任意のプログラムを対象にしたテストを構成できます。

Web、Windows ストア、Windows Phone など、基本的なプログラムの UI に対応した Visual Studio テンプレートを利用できます。こうしたテンプレートのうちいずれかからプロジェクトを作成するときは、新しいテストを記録するのかそれとも既存のテストを編集するのかについて、確認するダイアログ ボックスが Visual Studio に表示されます (図 6 参照)。[操作の記録、UI マップの編集、またはアサーションの追加] をクリックして [OK] をクリックしすると、ツール ウインドウが表示されます (図 7 参照)。これと同じダイアログ ボックスとツール ウィンドウは、新しい CUIT ファイルを (プロジェクト ファイルの [新しい項目] を使用して) プロジェクトに追加するたびに表示されます。ツール ウインドウが表示されたら、[記録の開始] ボタンをクリックして、Web ページやアプリ UI に対する操作の記録を開始できます。

コード化された UI テストの記録または編集を選択するための、Visual Studio に表示されるダイアログ ボックス
図 6 コード化された UI テストの記録または編集を選択するための、Visual Studio に表示されるダイアログ ボックス

アサーションの記録や編集を行うツール
図 7 アサーションの記録や編集を行うツール

記録が完了したら、詳しくてわかりやすいメタデータを追加し、[コードの生成] ダイアログ ボックスの下部にある [追加と生成] ボタンをクリックすると (図 8 参照)、コードが生成されます。

コード化された UI テストを生成するダイアログ ボックス
図 8 コード化された UI テストを生成するダイアログ ボックス

コード化された UI プロジェクトを作成して、コード化された UI テストをプロジェクトに追加すると、Visual Studio によって大量のコードが生成されます。ただし、Visual Studio によるテスト コードの生成方法は実に優れています。出力がクリーンで、特に生成されたコードの出力はクリーンです。Visual Studio では、ユーザーが特定のプログラムを使用しているときの操作を記録することで、コードを生成します。図 7 のツールボックスで [記録の開始] ボタンをクリックすると、ユーザーの操作がレコーダーで追跡され、同じ操作を実行するコードが生成されます。このコードを独自に記述してもかまいませんが、生成する方が簡単です。

[コード化された UI テスト ビルダー] を閉じると、Visual Studio によってファイルがいくつか生成されます。最初のファイルはコード化された UI テストファイルです。これは .cs ファイルで、ファイル内のクラスにはユーザーが UI を使用する際の手順と同じ手順が実行されるメソッドが記述されています。残りのファイルは、UIMap.uitest、UIMap.cs、および UIMap.designer.cs というマップ ファイルです。従来の Visual Studio デザイナーはコードが大量に生成されることで悪名が高かったものの、コード化された UI テストのマップ デザイナーは動作が大幅に向上しています。デザイナーが邪魔になることもなく、簡単にテストを編集できます。マップ デザイナーはいつでも編集可能です。編集するには .uitest ファイルを右クリックし、ショートカット メニューの [コード化された UI テスト ビルダーで編集] をクリックします。このようにすると、図 7 のツールボックスが起動します。ここでも、独自のテスト コードを記述してもかまいません。しかし、テスト コードの自作を決断する前に、次の 4 つのテストを実行する生成コードの抜粋をご覧ください。

  1. 新規顧客が正常に作成されることのテスト
  2. 名前を入力しなくても新規顧客が作成されることのテスト
  3. 住所を入力しなくても新規顧客が作成されることのテスト
  4. 無効な形式の郵便番号を入力しても新規顧客が作成されることのテスト

図 9 をご覧になるとわかるように、4 つのテストは UIMap.designer.cs ファイルに記述されています。また、コード化された UI テスト クラスのコードでこの 4 つのテストを連続して呼び出しています。

図 9 コード化された UI テスト ファイルとマップ デザイナー ファイルの内容の一部

// In the Coded UI Test C# file
[CodedUITest]
  public class CodedUITest1
  {
    [TestMethod]
    public void CodedUITestMethod1()
    {
      this.UIMap.CreateNewCustomerCorrectly();
      this.UIMap.CreateNewCustomerWithoutName();
      this.UIMap.CreateNewCustomerWithoutAddress();
      this.UIMap.CreateNewCustomerWihtoutValidPostalCode();       
    }
    // Some other framework code for Coded UI
  }
// In the Coded UI UIMap.Designer.cs file
public void CreateNewCustomerWithoutName()
    {
      #region Variable Declarations
      // Click 'Create New' link
      Mouse.Click(uICreateNewHyperlink, new Point(51, 8));
      // Type '' in 'Name' text box
      uINameEdit.Text = this.CreateNewCustomerMissingNameParams.UINameEditText;
      // Type '{Tab}' in 'Name' text box
      Keyboard.SendKeys(uINameEdit,
        this.CreateNewCustomerMissingNameParams.UINameEditSendKeys,
        ModifierKeys.None);
      // Type '234 Lane St' in 'Address' text box
      uIAddressEdit.Text =
        this.CreateNewCustomerMissingNameParams.UIAddressEditText;
      // Type '{Tab}' in 'Address' text box
      Keyboard.SendKeys(uIAddressEdit,
        this.CreateNewCustomerMissingNameParams.UIAddressEditSendKeys,
        ModifierKeys.None);
  }
}

コード化された UI テストは、他のテストとよく似ていますが、ユーザーによる UI 操作と同様に UI を操作することを対象としています。さいわいにも、コード化された UI 用のツールは、ユーザーの操作を記録して、同じ操作が行われるコードを生成することに優れています。いつでもテストを削除および再生成でき、安全な方法でコードを編集できます。また、単体テストと同様に、Visual Studio のテスト エクスプローラーからコード化された UI テストを実行することもできます。コード化された UI テストは、テスト エクスプローラーで単体テストの隣に表示されます。

ソフトウェアの品質を向上する

さまざまな形式でテストすることには実に大きなメリットがあります。簡便な Windows ストア アプリや Windows Phone アプリであってもテストは重要です。アプリ ストアで販売する予定の場合は特に重要になります。テスト済みのアプリは、ドキュメントと監査記録が揃っているため、動作を信頼できるアプリです。

xUnit や qUnit などのライブラリで単体テストを実行できます。また、Web アプリやネイティブ アプリには CUIT による UI オートメーション テストを使用できます。ユーザー受け入れテスト、統合テスト、システム テストなどの他の重要なテストについては、言うまでもありません。単体テストと UI テストという信頼できる組み合わせにこうしたテストを追加すれば、ソフトウェアの品質が著しく向上します。


Rachel Appel は 20 年を超える IT 業界での経験を持つマイクロソフトの元社員で、コンサルティング、執筆活動、および指導を行っています。彼女は Visual Studio Live!、DevConnections、MIX など、業界トップ クラスのカンファレンスで講演しています。専門分野は、マイクロソフトの各種開発ツールやオープン Web を重視したテクノロジとビジネスを連携させるソリューションの開発です。彼女のことをもっと知りたい方は、彼女の Web サイト rachelappel.com (英語) をご覧ください。

この記事のレビューに協力してくれた技術スタッフの Oren Novotony に心より感謝いたします。