Webのユニットテスト② .NET

前回の続きです。
Default.aspxのテストを簡単にする為にユーティリティクラスを作ってみます。


WebアプリケーションのテストはSeleniumを使ってテストしてみます。
Selenium RCは.NETにも対応しているので、Javaで作ったクラスを.NETに移植していきます。

WebTester.cs

SeleniumServerを起動し、Seleniumインスタンスを生成します。
Seleniumインスタンスを使って、ブラウザを操作しながらテストしていきます。

using System;
using System.Configuration;
using System.Diagnostics;

using Selenium;

namespace WebAndNDbUnitSampleTest
{
    public class WebTester
    {
        private ISelenium selenium;
        private string className;
        private string methodName;

        public void SetUp() {
            
            // テスト対象のサーバURL&ブラウザ設定をApp.configから読み込む
            string url = ConfigurationManager.AppSettings["SeleniumTest.Url"];
            string browser = ConfigurationManager.AppSettings["SeleniumTest.Browser"];

            // seleniumインスタンス起動
            this.selenium = new DefaultSelenium("localhost", 4444, browser, url);
            this.selenium.Start();
        }

        public void TearDown() {

            // 画面のスクリーンショットを保存
            CaptureScreenshot();

            // Seleniumインスタンスを停止
            this.selenium.Close();
            this.selenium.Stop();
        }

        public ISelenium GetSelenium() {
            return this.selenium;
        }

        public void Open(String url) {

            // アプリケーション名の取得
            string appName = ConfigurationManager.AppSettings["SeleniumTest.App"];

            // ブラウザを最大化で表示
            this.selenium.Open(appName + url);
            this.selenium.WindowMaximize();

            // 呼び出し側のクラス名&メソッド名を保持しておく
            StackTrace st = new StackTrace(true);
            this.className = st.GetFrame(2).GetFileName();
            this.methodName = st.GetFrame(2).GetMethod().Name;
        }

        public void CaptureScreenshot() {
            CaptureScreenshot(this.className + "." + this.methodName + ".png");
        }

        public void CaptureScreenshot(String fileName) {

            // スクリーンショット保存ディレクトリが指定されている場合に、スクリーンショットを保存する
            string ScreenshotDir = ConfigurationManager.AppSettings["SeleniumTest.ScreenshotDir"];
            if (ScreenshotDir != null) {

                // 画面のスクリーンショットを保存
                this.selenium.CaptureScreenshot(ScreenshotDir + "/" + fileName);
            }
        }
    }
}

App.config

WebTesterで使用するプロパティを定義しています。
他のブラウザでのテストも簡単に行えるように、定義情報を外だしにしておきます。

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <appSettings>
    
    <!-- テスト対象サーバのURL -->
    <add key="SeleniumTest.Url" value="http://localhost:8080/" />
    
    <!-- テスト対象のWEBアプリケーション名 -->
    <add key="SeleniumTest.App" value="" />
    
    <!-- テストで使用するブラウザ -->
    <add key="SeleniumTest.Browser" value="*iexplore" />
    
  </appSettings>
  <connectionStrings>
    <add name="DataBase" connectionString="Server=XXXXX\SQLEXPRESS;Initial Catalog=sample;User ID=sa;Password=hogehoge;" providerName="System.Data.SqlClient"/>
  </connectionStrings>
</configuration>

HtmlTableAssert.cs

指定されたHTMLテーブルの行数、列数、表示値をアサートするクラスです。
Seleniumにありそうなクラスですが、ないみたいなので作ってみました。


SeleniumではHTMLの要素をXPATHで指定します。
例えば、class名がitsというtableの1行2列目にある要素を指定したい場合は、

xpath=//table[@class='its']//tr[1]/td[2]

となります。

using System;

using NUnit.Framework;
using Selenium;

namespace WebAndNDbUnitSampleTest
{
    class HtmlTableAssert
    {
        private HtmlTableAssert()
        {
        }

        // テーブルの表示データを確認
        public static void AssertEquals(ISelenium selenium, string tablePath, string[][] expected)
        {

            for (int i = 0; i < expected.GetLength(0); i++)
            {
                // 比較対象の行が存在するか
                if (!selenium.IsElementPresent(GetRow(tablePath, i)))
                {
                    Assert.Fail("Different number of Rows : " + expected.GetLength(0) + " : " + (i + 1));
                }

                for (int j = 0; j < expected[i].GetLength(0); j++)
                {
                    // 比較対象の列が存在するか
                    if (!selenium.IsElementPresent(GetColumn(tablePath, i, j)))
                    {
                        Assert.Fail("Different number of columns : " + expected[i].GetLength(0) + " : " + (j + 1));
                    }

                    // 列の表示値を確認
                    string actual = selenium.GetText(GetColumn(tablePath, i, j));
                    Assert.AreEqual(expected[i][j], actual, "Difference on row:" + (i + 1) + ", column:" + expected[i][j]);
                }

                // 比較対象の列数が期待値を超えていないか
                if (selenium.IsElementPresent(GetColumn(tablePath, i, expected[i].GetLength(0))))
                {
                    Assert.Fail("Over number of columns : " + expected[i].GetLength(0));
                }
            }

            // 比較対象の行数が期待値を超えていないか
            if (selenium.IsElementPresent(GetRow(tablePath, expected.GetLength(0))))
            {
                Assert.Fail("Over number of rows : " + expected.GetLength(0));
            }
        }

        private static string GetRow(String tablePath, int row)
        {
            //return "xpath=//" + tablePath + "//tr[" + (row + 1) + "]";
            return "xpath=//" + tablePath + "//tr[" + (row + 2) + "]"; // ヘッダ行は対象外
        }

        private static string GetColumn(String tablePath, int row, int column)
        {
            return GetRow(tablePath, row) + "/td[" + (column + 1) + "]";
        }
    }
}