ささいなことですが。

Windowsアプリテスト自動化ライブラリFriendly開発者の日記です。

Friendly ハンズオン 13 アプリケーションドライバ -その3-

前回までで、通常操作に関しての実装は完了して、思い通りにアプリを操作できるところまで行きました。
で、今回はテスト自動化で困りがちな点の対応を考えていきます。これもアプリケーションドライバ層で吸収すれば良いと思います。

ここから先はAppDriverクラスを拡張していきます。
そんなに難しいことではなくチップス的なテクニックです。

モーダルで固まった場合の対応

タイムアウトさせるしかないですね。
時間が来たら対象を強制終了します。
こんな感じです。

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace EmployeeManagementDriver
{
    public class Killer
    {
        Task _killer;
        bool _executing = true;
        bool _kill;
        public int Timeup { get; set; }

        public Killer(int timeup, int processId)
        {
            Timeup = timeup;
            _killer = Task.Factory.StartNew(() =>
            {
                Stopwatch watch = new Stopwatch();
                watch.Start();
                while (_executing)
                {
                    if (Timeup < watch.ElapsedMilliseconds)
                    {
                        Process.GetProcessById(processId).Kill();
                        _kill = true;
                        break;
                    }
                    Thread.Sleep(10);
                }
            });
        }

        public bool Finish()
        {
            _executing = false;
            _killer.Wait();
            return !_kill;
        }
    }
}

AppDriverで使ってみましょう。

using Codeer.Friendly;
using Codeer.Friendly.Dynamic;
using Codeer.Friendly.Windows;
using Codeer.Friendly.Windows.Grasp;
using Ong.Friendly.FormsStandardControls;
using System.Diagnostics;

namespace EmployeeManagementDriver
{
    public class AppDriver
    {
        WindowsAppFriend _app;
        public MainFormDriver MainForm { get; private set; }
        public Killer Killer{ get; private set; }

        public AppDriver()
        {
            var process = Process.Start("EmployeeManagement.exe");
            _app = new WindowsAppFriend(process);
            MainForm = new MainFormDriver(new WindowControl(_app, process.MainWindowHandle));

            //5分でタイムアウトにする
            Killer = new Killer(1000 * 60 * 5, process.Id);
        }

        public void Release()
        {
            if (Killer.Finish())
            {
                //タイムアップ終了していない場合は自ら終了させる
                Process.GetProcessById(_app.ProcessId).CloseMainWindow();
            }
        }
    }
}

時間は共通で定義しておいて、特別なテストだけ延長するとかにすると良いと思います。
あんまり、細かく設定するのは面倒ですので、テストが複雑化してしまいます。
正常パスでは発生しないケースなので。
ちょっとテストしておきましょう。

[TestMethod]
public void TestTimeup()
{
    _app.Killer.Timeup = 1000;
    try
    {
        var addForm = _app.MainForm.ButtonAdd_EmulateClick();
        addForm.ButtonEntry_EmulateClickAndClose();
        Assert.Fail();
    }
    catch (FriendlyOperationException) { }
}

これで、モーダルダイアログで止まっても、5分後には次のテストに移れます。

実はもう一つやることがあります。

それは、対象アプリのライフサイクルの管理です。
次回はそれをやります。

サンプルコード修正

2015/1/30
モーダルダイアログの書き方が適切でなかったので修正しました。それにより、一部ここに書くのは不適当な記事を削除しました。また別の機会に解説します。