UWSCでデスクトップアプリの自動テスト

こんにちは。弥生の内山です。
この記事はMisoca+弥生+ALTOA Advent Calendar 2018の21日目の記事です。

はじめに

弥生の主力製品は弥生会計をはじめとしたWindowsアプリです。
弥生ではテストの自動化を進めていますが、結合テスト、統合テストといった工程では、やはり手動でのテストが多く、テストの実施のためにかなりの工数が費やされています。
この工数を少しでも削減しようと、UWSCというツールを使って手動テストを自動化する取り組みを進めてきましたので、その内容をご紹介します。

UWSCとは

UWSCはWindowsの自動操縦ツールです。
20年近く歴史のあるツールで、キーボードやマウスの操作を記録して再生する機能だけでなく、VBA風の独自のスクリプト言語を搭載しており、スクリプトを作成することによってかなり複雑な自動操作を行わせることができます。

やったこと

弥生のWindowsアプリの1つである弥生給与を対象に、手動テストを自動化するための取り組みを進めてきました。
ここでは、具体的にどのようなことを行ってきたかをご紹介します。

まずは1つ自動化してみる

あるテストシナリオに対して、まずはベタにUWSCのスクリプトを書き進めて自動テストができるようにしました。
従来通りのテスト手順書の作成と比較するとかなり時間がかかりましたが、このときは「このテストをどうしても自動化したい」というオーダーを受けていたので、十分に工数を確保することができました。

ライブラリ化する

スクリプトを書き進めていくと、やはり処理を関数などに切り出して整理したくなってきます。
自動化を始めたときから、チームメンバーや他のチームにも展開することを念頭に置いていたので、共通で使えると思った処理は積極的に関数化し、ライブラリとしてまとめていきました。

以下にライブラリを使ったスクリプトの例を示します。

call yayoi-uwsc-lib\kyuyo

事業所データを新規作成する("弥生株式会社", "H3001")

call文は他のUWSCスクリプトファイルを取り込みます。
ここでは、yayoi-uwsc-lib\kyuyo.uwsというスクリプトファイルを読み込んでいます(拡張子は省略可能です)。
このスクリプトファイルがライブラリ本体になります。

「事業所データを新規作成する」は、前述のライブラリに含まれる関数です。
この関数を実行すると、起動している弥生給与の新規作成ウィザードを開き、「弥生株式会社」という事業所名で、導入月度を平成30年1月に設定したデータファイルを作成します。
参考: 給与データの新規作成
弥生給与の新規作成ウィザードは画面数が多く、手動で進めるのは面倒なのですが、この関数はそれを数秒で完了させてくれます。

ここで「え?日本語名の関数…?」と思われた方も多いかもしれません。
ライブラリ中では、関数などの名前に積極的に日本語名を使うようにしました。
理由としては以下です。

  • テスト対象が日本語の製品である: 弥生の製品は基本的に日本国内向けに作られており、画面上に配置される文字列も基本的に日本語です。それらをいちいち英語に翻訳するには時間がかかりますし、訳が適切でないなどの理由で意図が伝わらない可能性も考えられます。そのため、日本語は日本語のまま使った方がよい、という判断をしました。
  • スクリプトを手順書として読めるようにしたい: スクリプトを見た人が、それを手順書として内容を把握できるようにしておきたい、という狙いの他にも、手動テスト用のテスト手順書をベースにスクリプトを作成したとき、スクリプトの内容が手順書と乖離していないことを確認しやすくしたい、と考えました。

テストフレームワークを作成する

ここまでで手動テストをある程度自動化できてきましたが、それを手動テストの代替として使うには、スクリプトがきちんとテストとして振る舞える仕組みが必要だと考えました。
「きちんとテストとして振る舞える」とは、具体的には以下のことだと考えました。

  1. 期待値と実際の値を比較できる
  2. テストの実施結果からレポートを作成できる
  3. 期待通りにテスト対象が動作しなかった場合にテストを終了できる

1は他のプログラミング言語におけるassertのようなものがあればよさそうです。

2は1の結果などを保存しておき、テスト終了時に整形してprintするようにします。
他のプログラミング言語のユニットテストには、結果を詳細に表示しないものもありますが、本件では開発プロセス中のテストフェーズで実施することを想定しているため、実施に関する情報(日時、OS等)も含めてなるべく詳細なレポートを作成するようにします。

3がやや問題で、UWSCではexitexit文を実行するとスクリプトの実行を強制終了させることができるのですが、そうすると2のレポート作成が実行されずに終了してしまうので、テスト実施者は何が起きたのかわからなくなってしまいます。
テストが途中で終了されたらそのことがわかり、レポートも必ず生成されるような仕組みが必要です。

これらの条件を満たすような仕組みを実装していったところ、結果的にテストフレームワークのようなものができあがりました。
このテストフレームワークを使ったスクリプトの例を以下に示します。

// テストフレームワークの読み込み
call yayoi-uwsc-lib\testing

// テスト開始処理
try; try; TEST.START()

// ↓ここからテストスクリプト本体を記述する
TEST.TESTSECTION("テスト1")

dim a = 1
TEST.TESTCASE("テスト1-1", a, 1)
TEST.TESTCASE("テスト1-2", a, 2)
// ↑ここまでテストスクリプト本体を記述する

// テスト終了処理
except; TEST.FAIL(TRY_ERRMSG + " @ " + TRY_ERRLINE); endtry
finally; TEST.END(); endtry

「テスト開始処理」と「テスト終了処理」がだいぶ呪文になっていますが…。
これはUWSCのtry-except構文と、try-finally構文を入れ子にしてあるものを、複文でまとめて書いてあります。
きちんと整形すると、以下のようになります。

try
  try
    TEST.START()
    // テスト本体...
  except
    TEST.FAIL(TRY_ERRMSG + " @ " + TRY_ERRLINE)
  endtry
finally
  TEST.END()
endtry

まずTESTというのが、UWSCのモジュール定義を使って書かれたモジュールです。
UWSCのモジュールは、シングルトン限定のクラスのようなもの、と考えてください。
このTESTモジュールがテストフレームワークの実装になります。

TESTモジュールは以下のような関数を保有しています。

  • START() : テストの開始処理を行う。開始時刻の記録など。
  • END() : テストの終了処理を行う。レポートの出力など。
  • FAIL(エラーメッセージ) : 実行した時点でテストを失敗として強制終了させる
  • TESTCASE(テストケース名, 期待値, 実際の値) : 値の比較を行い、その結果をレポートに追加する。
  • TESTSECTION(セクション名) : 上記TESTCASEをグループ分けする。今のところはレポートの表のヘッダを出力しているだけ…。

テスト本体は、try-excepttry節で実行されます。
これにより、テストの処理中にエラーが発生した場合、except節に飛ばされ、TEST.FAILが実行されます。
TEST.FAILは、テストが失敗であるとレポートに出力するよう設定して、テストを強制終了させます。

外側のtry-finallyは、テストの最後に必ずTEST.ENDを実行させるためのものです。
TEST.ENDは、レポートの出力処理などを行います。
なお、finally節が必ず実行されるように、テストフレームワーク内部ではOPTFINALLYオプションを設定しています。

このような書き方にすることによって、スクリプト終了時に必ずテスト結果のレポート出力が行われるようにしました。
実際に上記のスクリプトを実行すると、UWSCのログウィンドウに以下のようにレポートが出力されます。

UWSCのログウィンドウにレポートのテキストが出力される

レポートのテキストはTracのWiki文法にて整形するようにしてみました。
(弥生ではTracをよく使っています。参考: 6日目の記事
レポートをそのままTracのWikiやチケットにコピペするだけで、テストの実施結果を共有できるようにすることを狙いました。
実際にTracに貼り付けると、以下のようになります。

レポートをTracに貼り付けた様子

テストスクリプトのテンプレートを作成する

これらのライブラリやテストフレームワークを利用してテストを書こう、と思った場合、例えば前述のテストフレームワークの呼び出しなどをゼロから書くのは難しいため、スクリプトを書き始めるためのテンプレートを用意しました。
以下に、テンプレートの抜粋を示します。

//******************************************************************************
// (このスクリプトが何を行うかを説明する)
//******************************************************************************

//==============================================================================
// ライブラリの読み込み
//==============================================================================

call yayoi-uwsc-lib\kyuyo
call yayoi-uwsc-lib\testing

// ↓他にファイルを読み込む場合はここに書く↓


//==============================================================================
// メインスクリプト
//==============================================================================

// テスト開始処理
try; try; TEST.START()

//------------------------------------------------------------------------------
// ↓ここからテストスクリプト本体を記述する↓
//------------------------------------------------------------------------------



//------------------------------------------------------------------------------
// ↑ここまでテストスクリプト本体を記述する↑
//------------------------------------------------------------------------------

// テスト終了処理
except; TEST.FAIL(TRY_ERRMSG + " @ " + TRY_ERRLINE); endtry
finally; TEST.END(); endtry

//==============================================================================
// 関数・モジュール定義
//==============================================================================

// ↓このスクリプトで使う関数やモジュールの定義はここに書く↓

ガイドコメントに従って書いていくことで、ある程度スクリプトを書きやすくすることを狙いました。
ここまでで、他の人がテストを書くために使う道具を準備できました。

チームに展開する

テストをチームメンバーに書いてもらうために、チームに展開する活動を行いました。
具体的には以下のようなことです(といっても最低限やるべきようなことばかりで、特別なことはしていないです)。

  • 作り方を文書化する:テストの書き方や、テストを書いていく上でのノウハウなどを文書にして共有しておく。
  • 説明する: 説明会を開き、上記の文書をもとに、活動の狙いなども含めてチームに説明。
  • サポートする: スクリプトの作成でわからないことや困ったことがあれば、いつでも相談に乗れるようにしておく。

この記事の執筆時点では、チームメンバーがいくつかのテストシナリオについて自動化を進めてくれている…といった状況です。
この取り組みがどれくらいの効果を出せるかは、まだこれからの話になります。

おまけ:UWSCの選定について

UWSCは、現在開発が停止している状態であり、今後のアップデートやサポートは期待できない状況にあります(参考)。
そんな状況で、このままUWSCを使い続けてよいのか? ということについてはかなり悩みました。
結局、以下のような理由で、UWSCを当分は使い続けることにしました。

  • 配布自体は続いているので、現状のまま使い続けることは可能
  • 最悪、乗り換える先がある: 同様の自動操縦ツールにメジャーなものとしてAutoItがあり、これはUWSCでできることはほぼできるので、代替は可能

おわりに

UWSCを使ってテストを自動化する取り組みをご紹介してきました。
もしみなさまの何かの参考になれば幸いです…。

明日は@yusuke_yayoiさんの「AWS AppSyncについて勉強してみた」です。楽しみですね!