a2 Tech blog

試したこと・調べたこと・感じたことを発信するITエンジニアの日記です。仕事とは直接関係ないけど興味あることを模索していきます。

Coded UI Test × Data Driven Test

f:id:ninna2:20170409015346j:plain

しばらくCoded UI Test(CUIT)から離れてましたが、久々に試してみたのでメモしておきます。今回は、CUITにData Driven Test(DDT)を組み合わせて、外部定義ファイル(CSV)からデータを読み込んで様々なバリエーションのデータでテストを実行する事を目指します。

基本的なCUITを定義

毎回テスト対象のサイトとして使わせてもらってますが「日本Seleniumユーザーコミュニティ」のテスト用サイト http://example.selenium.jp/reserveAppを使っていきます。CUITの基本的なことは過去記事に書いてますのでよければ参照してみてください。

ninna2.hatenablog.com

最初にコード化されたUIテストのプロジェクトを作成します。

UIテストビルダーを使用してテストオペレーションを記録していきます。ブラウザ(IE)を開いてテスト用サイトを表示するところまでは、TestInitializeで実施するようにし、予約画面に入力するところからTestMethodにテストオペレーションを記録してます。

ちょっと長いですが、このような感じで自動生成コードが出来上がります。

CodedUITest1.cs(一部)

    [TestMethod]
    public void CodedUITestMethod1()
    {
        this.UIMap.datadriventest_form_input_01();
    }

UIMap.Designer.cs(一部)

    /// <summary>
    /// datadriventest_form_input_01 - このメソッドにパラメーターを渡すには 'datadriventest_form_input_01Params' を使用します。
    /// </summary>
    public void datadriventest_form_input_01()
    {
        #region Variable Declarations
        HtmlEdit uIReserve_mEdit = this.UIGoogleInternetExplorWindow.UI予約情報入力Document.UIReserve_mEdit;
        HtmlEdit uIReserve_dEdit = this.UIGoogleInternetExplorWindow.UI予約情報入力Document.UIReserve_dEdit;
        HtmlEdit uIReserve_tEdit = this.UIGoogleInternetExplorWindow.UI予約情報入力Document.UIReserve_tEdit;
        HtmlEdit uIHCEdit = this.UIGoogleInternetExplorWindow.UI予約情報入力Document.UIHCEdit;
        HtmlRadioButton uIBFRadioButton = this.UIGoogleInternetExplorWindow.UI予約情報入力Document.UIBFRadioButton;
        HtmlCheckBox uIPlan_aCheckBox = this.UIGoogleInternetExplorWindow.UI予約情報入力Document.UIPlan_aCheckBox;
        HtmlEdit uIGnameEdit = this.UIGoogleInternetExplorWindow.UI予約情報入力Document.UIGnameEdit;
        HtmlButton uI次へButton = this.UIGoogleInternetExplorWindow.UI予約情報入力Document.UI次へButton;
        HtmlButton uI確定Button = this.UIGoogleInternetExplorWindow.UI予約内容確認Document.UI確定Button;
        BrowserWindow uIGoogleInternetExplorWindow = this.UIGoogleInternetExplorWindow;
        #endregion

        // 'reserve_m' テキスト ボックス に '5' を入力
        uIReserve_mEdit.Text = this.datadriventest_form_input_01Params.UIReserve_mEditText;

        // 'reserve_d' テキスト ボックス に '10' を入力
        uIReserve_dEdit.Text = this.datadriventest_form_input_01Params.UIReserve_dEditText;

        // 'reserve_t' テキスト ボックス に '2' を入力
        uIReserve_tEdit.Text = this.datadriventest_form_input_01Params.UIReserve_tEditText;

        // 'hc' テキスト ボックス に '4' を入力
        uIHCEdit.Text = this.datadriventest_form_input_01Params.UIHCEditText;

        // 'bf' オプション ボタン を選択
        uIBFRadioButton.Selected = this.datadriventest_form_input_01Params.UIBFRadioButtonSelected;

        // 'plan_a' チェック ボックス を選択
        uIPlan_aCheckBox.Checked = this.datadriventest_form_input_01Params.UIPlan_aCheckBoxChecked;

        // 'gname' テキスト ボックス に 'Test01' を入力
        uIGnameEdit.Text = this.datadriventest_form_input_01Params.UIGnameEditText;

        // クリック '次へ' ボタン
        Mouse.Click(uI次へButton, new Point(18, 9));

        // クリック '確定' ボタン
        Mouse.Click(uI確定Button, new Point(22, 10));

        // ブラウザー ウィンドウで [閉じる] を実行
        uIGoogleInternetExplorWindow.Close();
    }

この時点で、実行すればテストオペレーションを記録した通りに実行されてテストが正常終了するはずです。基本的な部分の準備はここまでです。

DDTのInputData(CSV)を作成

DDTの入力は、ファイルでもデータベースでも良いのですが、今回は簡単に実行できるCSV形式のファイルをInputDataとしていきたいと思います。InputDataの場所はプロジェクトフォルダの"In"フォルダとします。

私の場合は、物理的には下記のフォルダは以下です。各自の環境で読み替えてくださいね。

C:\workspace\CodedUITestProject_ddt\CodedUITestProject_ddt\In

ファイル名は任意でOKです。 “testdata_01.csv"とします。

CSVファイルの中身を定義していきます。とりあえず複数回テストを回していきたいのでデータを2件登録します。CSVのヘッダは、HTMLタグのnameで定義してみました。わかりやすい名前なら何でもOKです。データを取り出す際のKeyになります。ファイルはANSIで保存します。メモ帳などで1回ファイルを開き名前をつけて保存で、"ANSI"を指定して上書きしておいてください。こうしないと日本語が文字化けして読めなかったです。

testdata_01.csv

reserve_y,reserve_m,reserve_d,reserve_t,hc,breakfast_on,breakfast_off,plan_a,plan_b,gname
2017,5,19,2,4,false,true,true,true,"ゲスト01"
2017,5,20,2,1,false,true,true,true,"ゲスト02"

InputDataの準備は完了です。

テストメソッドでCSVファイルを読み込むようにカスタマイズ

テストメソッドをDDT実行するようにカスタマイズしていきます。といっても、メソッドの中身を書き換える必要はなく、メソッドのAttributeでInputDataのコピーと、データソースでの読み込みを定義すればOKです。

CodedUITest1.cs

    [TestMethod]
    [DeploymentItem("CodedUITestProject_ddt\\In\\testdata_01.csv")]
    [DataSource("Microsoft.VisualStudio.TestTools.DataSource.CSV", "|DataDirectory|\\testdata_01.csv", "testdata_01.csv", DataAccessMethod.Sequential)]
    public void CodedUITestMethod1()
    {
        this.UIMap.datadriventest_form_input_01();
    }

“|DataDirectory|"という代替文字列を使用するために、AppDomain.SetDataにてAppDomainのDataDirectoryプロパティを設定する必要があります。設定はテスト実行時に1度だけ呼び出してほしいので、AssemblyInitializeを定義してその中で実行するようにします。

CodedUITest1.cs

    [AssemblyInitialize]
    public static void AssemblyInitialize(TestContext context)
    {
        AppDomain.CurrentDomain.SetData("DataDirectory", context.TestDir);
    }

ここまで出来ればテスト実行時にTestContextのDataRowにCSVのデータが1行ずつ読み込まれてテストが複数回走るようになります。

自動生成コードのカスタマイズ

このままでは、せっかく読み込んだCSVデータを使っていないので、読み込んだデータを使用してテストが実行されるように、自動生成されたコードをカスタマイズしていきます。まず、UIMap.Designer.csから UIMap.csにコードを移動していきます。コードを移すと以降はUIテストビルダーによる変更は出来ないので注意してください。

自動生成コードを、UIMap.csに移せたら、カスタマイズしていきます。UIMap.Designer.csにある状態でカスタマイズしてもテスト実行時に上書きされてしまうので必ずUIMapに移してくださいね。まず、TestContextを引数に追加します。テストオペレーションで画面の入力を行なっている箇所をTestContext.DataRowから取得した値から設定するように変更していきます。適時、Castしておきましょう。

例
[文字列の場合]
context.DataRow["ColumnName"].ToString();

[数字の場合]
int.Parse(context.DataRow["ColumnName"].ToString());

[真偽値の場合]
Boolean.Parse(context.DataRow["ColumnName"].ToString());

UIMap.cs

    /// <summary>
    /// datadriventest_form_input_01 - このメソッドにパラメーターを渡すには 'datadriventest_form_input_01Params' を使用します。
    /// </summary>
    public void datadriventest_form_input_01(TestContext context)
    {
        #region Variable Declarations
        HtmlEdit uIReserve_mEdit = this.UIGoogleInternetExplorWindow.UI予約情報入力Document.UIReserve_mEdit;
        HtmlEdit uIReserve_dEdit = this.UIGoogleInternetExplorWindow.UI予約情報入力Document.UIReserve_dEdit;
        HtmlEdit uIReserve_tEdit = this.UIGoogleInternetExplorWindow.UI予約情報入力Document.UIReserve_tEdit;
        HtmlEdit uIHCEdit = this.UIGoogleInternetExplorWindow.UI予約情報入力Document.UIHCEdit;
        HtmlRadioButton uIBFRadioButton = this.UIGoogleInternetExplorWindow.UI予約情報入力Document.UIBFRadioButton;
        HtmlCheckBox uIPlan_aCheckBox = this.UIGoogleInternetExplorWindow.UI予約情報入力Document.UIPlan_aCheckBox;
        HtmlEdit uIGnameEdit = this.UIGoogleInternetExplorWindow.UI予約情報入力Document.UIGnameEdit;
        HtmlButton uI次へButton = this.UIGoogleInternetExplorWindow.UI予約情報入力Document.UI次へButton;
        HtmlButton uI確定Button = this.UIGoogleInternetExplorWindow.UI予約内容確認Document.UI確定Button;
        BrowserWindow uIGoogleInternetExplorWindow = this.UIGoogleInternetExplorWindow;
        #endregion

        // 'reserve_m' テキスト ボックス に入力
        uIReserve_mEdit.Text = context.DataRow["reserve_m"].ToString();

        // 'reserve_d' テキスト ボックス に入力
        uIReserve_dEdit.Text = context.DataRow["reserve_d"].ToString();

        // 'reserve_t' テキスト ボックス に入力
        uIReserve_tEdit.Text = context.DataRow["reserve_t"].ToString();

        // 'hc' テキスト ボックス に入力
        uIHCEdit.Text = context.DataRow["hc"].ToString();

        // 'bf' オプション ボタン を選択
        uIBFRadioButton.Selected = Boolean.Parse(context.DataRow["breakfast_off"].ToString());

        // 'plan_a' チェック ボックス を選択
        uIPlan_aCheckBox.Checked = Boolean.Parse(context.DataRow["plan_a"].ToString());

        // 'gname' テキスト ボックス に 'Test01' を入力
        uIGnameEdit.Text = context.DataRow["gname"].ToString();

        // クリック '次へ' ボタン
        Mouse.Click(uI次へButton, new Point(18, 9));

        // クリック '確定' ボタン
        Mouse.Click(uI確定Button, new Point(22, 10));

        // ブラウザー ウィンドウで [閉じる] を実行
        uIGoogleInternetExplorWindow.Close();
    }

これで概ね完了なのですが、引数にTestConextを追加したので、呼び出し元がビルドエラーになっているはずです。ですので、呼び出し元でちゃんとTestCotextを渡してあげるようにします。

    [TestMethod]
    [DeploymentItem("CodedUITestProject_ddt\\In\\testdata_01.csv")]
    [DataSource("Microsoft.VisualStudio.TestTools.DataSource.CSV", "|DataDirectory|\\testdata_01.csv", "testdata_01.csv", DataAccessMethod.Sequential)]
    public void CodedUITestMethod1()
    {
        this.UIMap.datadriventest_form_input_01(TestContext);
    }

これでやっと全部完了です。

DDTの実行

DDTは特別な実行は必要なく、通常のテスト実行と同じように実行してください。実行すると、データ行 0、データ行 1 と入力値違いのテストが2回実行されます。

上手くいきましたね。これでこの画面に対しては、CSVファイルのデータを追加、変更してあげるだけで、テストケースを増やすことができます。様々なバリエーションを試すのが容易になりました。

実は1つだけ問題点があります。私も原因の特定ができていないのですが、自動キャプチャ取得の設定をしておいても、出力されるHTMLファイルが、UITestActionLog.htmlという1ファイルで同一名になり、最後の実行したケースしか残らないのです。どうしたら全ての画面キャプチャが取れるのかわかる人教えてください。

色々調べてるけどわからないー。解決策見つかったら更新します。