【Squishノウハウ】Docker + Squishでテストを並列実行しよう

この記事は、Concurrent Test Execution with Squish and Dockerの抄訳です。

テストを早く、頻繁に実行する。言うは易く行うは難し、でしょうか?

Docker Swarmを使えば、すべてのテストを簡単に並行して実行でき、テストのNOKを発見するまでの時間を大幅に短縮することができます。

テストの実行には時間がかかります。特にGUIテストでは、テスト対象のアプリケーションが追いつくのを待つために、テストスクリプト内に多くの同期ポイントを必要とする傾向があります。このため、テストの実行時間が長くなり、1つのテストケースの実行時間が5分以上になることもあります。数百、数千のテストケースがある場合、テストスイート全体を毎回実行するのは不可能であることは明らかです:すべてのテストケースを1回実行すると、12時間以上かかることもあります。

さらに、ユーザーインターフェイスは複雑化し、ユーザーに公開される機能も増え続けています。そのため、テストスイートは大きくなり、製品の品質を測定するために必要なテストの量も増え続けています。そのうちに、すべてのテストケースを順次実行するだけでは、スケールしないことが分かってきます。もし、作業をきれいにパッケージ化して、複数の作業者に分散させる方法があれば...。

Docker Swarmの強み

Docker(英語)は、アプリケーションとその依存関係をすべて単一のファイル(「イメージ」と呼ばれる)にパッケージ化し、「コンテナ」を起動するために使用できる、人気かつ無料のツールです。以前のブログ記事では、テスト対象のアプリケーションとSquishのテストスクリプトを1つのイメージにパッケージ化し、GUIテストを実行するコンテナのテンプレートとして使用する方法を紹介しました。

Docker Swarm(英語)を使用することで、これをさらに一歩進めることができます。Dockerをswarmモードで使用する場合、Dockerエンジンのグループ(通常はネットワークで接続された異なるコンピュータ上で動作)は1つのユニットとして動作します。Docker Swarmを魅力は下記のようにいくつかあります:

  • Dockerの標準的なインストールの一部であり、追加の設定は必要ありません。
  • 使用方法は複雑ではありません。
  • マシンに負荷がかからないように、タスクを自動的に分散させることができます。

これらの特徴をどのように活かせるのか、以下でご紹介します。

はじめに

Docker Swarmを使った分散型GUIテスト自動化のセットアップには、3つの重要な要素が必要です:

  1. Dockerがインストールされたコンピュータが複数必要です。技術的には、1台のマシンでも可能ですが、あまり同時並行性はないでしょう。少なくとも2台のマシンがあれば、たとえそれが仮想マシンであっても、もっとやりやすくなります。心配しないでください:この決定を前もってする必要はありません、マシンを追加してスワームを大きくすることは、後で見るように、1つのコマンドを実行するだけです。
  2. テストスイートは、テストケース間の依存関係がない(あるいは少なくともほとんどない)ことが望ましいです。つまり、あるテストを実行した結果が、その前に実行されたテストに依存しないようにしなければなりません。テストケース間の依存関係をなくすことで、任意の順番で実行したり、任意のテストケースをスキップしたり、異なるコンピュータで1つずつ実行することができます。テストケースの依存関係を取り除くことの利点とその方法については、テストケース間の依存関係の発見と修正に関する記事をご覧ください。
  3. テストを実行するために必要なものがすべて含まれているDockerイメージ(または連携して使用するイメージのセット)が必要です。これを用意する方法は、弊社のブログ記事【Squishノウハウ】DockerコンテナでSquishテストを実行するをご参照ください。

アーキテクチャ

Dockerセットアップのアーキテクチャはシンプルです:

  • 1台のコンピュータがスウォームの「マネージャー」として、作業を分散させる
  • 他のコンピュータは、実際の作業を行う「ワーカー」として機能する。一般的に、マネージャー・コンピュータもワーカーと考慮される。つまり、仕事を分配するだけでなく、その管理までを行う。

スウォームのワーカーノードが作業をするためには、テストイメージを手に入れる必要があります。これは、スウォームで使用するDockerイメージのハブとして機能する共有Dockerレジストリ(英語)を使用することで実現可能になります。テスト実行をswarmにディスパッチする前に、テストイメージをレジストリにプッシュして、すべてのノードがテスト対象のアプリケーションのまったく同じビルド(とテストスクリプトのまったく同じバージョン)を使用できるようにします。

最後に、テストレポートを集中管理する場所が必要です。Squish Test Centerは、すべてのテストレポートの中央リポジトリとして機能し、テストレポートのレビューと分析に便利なWeb UIを提供するため、今回のユースケースに適切なツールとなります。すべてのワーカーノードは、テスト結果をSquish Test Centerへ直接プッシュします。

プライベートDockerレジストリ

まずは、テストイメージを保持(共有)するためのプライベートDockerレジストリ(英語)をセットアップしましょう。これは非常に簡単です。レジストリをホストするために必要なロジックは、専用のDockerイメージに含まれています。そのイメージに基づいてコンテナを起動すればよいだけなのです。

スウォームの一部となるすべてのマシンからアクセス可能なコンピュータにログインし、以下を実行します。

$ docker run -d -p5000:5000 --restart always registry:latest
Unable to find image 'registry:latest' locally
latest: Pulling from library/registry
e95f33c60a64: Pull complete
4d7f2300f040: Pull complete
35a7b7da3905: Pull complete
d656466e1fe8: Pull complete
b6cb731e4f93: Pull complete
Digest: sha256:da946ca03fca0aade04a73aa94b54ff0dc614216bdd1d47585f97b4c1bdaa0e2
Status: Downloaded newer image for registry:latest
ddd5e8c1fb7815157fb9f9413f014b94e11bc1f9cf7dddb41a8267c2da64727f

これにより、レジストリイメージがダウンロードされ、バックグラウンドでコンテナが起動され(-d)、ポート5000でレジストリサービスが利用できるようになります。私の場合、IPアドレスが192.168.178.47のコンピュータでコマンドを実行したので、Dockerレジストリは192.168.178.47:5000で利用可能になりました。

しかし、このレジストリは実際にはローカルへの展開のためのもので、プレーンな(暗号化されていない)HTTP通信を使用します。ワーカーマシン上で動作するDockerインスタンスが実際にそのレジストリを使用するようにするには、それを「安全でないレジストリ」のリストに追加する必要があります。そのためには、各ワーカーマシンにログインして、/etc/docker/daemon.jsonファイルに以下が含まれるようにします:

{
"insecure-registries" : ["192.168.178.47:5000"]
}

もちろん、実際に記述するIPアドレスは、192.168.178.47ではなく、上記のdocker runコマンドを実行したコンピュータのIPアドレスを使用するようにしてください。

あとは、新しく作成したレジストリにテスト用イメージを入れるだけです。SquishとDockerの使用に関する最初の記事に基づいて作成したテスト用イメージは、addressbook-squishと呼ばれます。このイメージをレジストリに追加するには、2つのステップを実行する必要があります:

  1. 既存のDockerイメージにタグを付け、ホスト/ポート情報をタグ名に入れ込みます:

   $ docker tag addressbook-squish 192.168.178.47:5000/addressbook-squish

  1. イメージをリポジトリにプッシュします:

   $ docker push 192.168.178.47:5000/addressbook-squish

ここでも、上記にある192.168.178.47をレジストリコンテナを起動した実際のコンピュータのIPアドレスに置き換えるようにしてください。

テストイメージを提供するセントラルレジストリができたので、そのイメージにアクセスするためのDockerノードのスワームを作成しましょう。

スウォームを作成する

まず、スウォームの「マネージャー」として機能するマシンを選びます。マネージャノードは、すべてのワーカーノードからアクセス可能である必要があり、作業のディスパッチやスウォームの状態の検査を行うマシンです。便宜上、このタスクにはレジストリをホストするために選んだコンピュータと同じものを選ぶことにします。

コンピュータをスワームモードにし、マネージャーとして機能させるには、docker swarmコマンドを使用する必要があります:

$ docker swarm init
Swarm initialized: current node (d0tcyemebrcgxjrq6xihz3zoe) is now a manager.

このスウォームにワーカーを追加するには、以下のコマンドを実行します:

$ docker run -d -p5000:5000 --restart always registry:latest
Unable to find image 'registry:latest' locally

docker swarm join --token SWMTKN-1-04u5uytbvhf6j0vvosxgw00zk3x50mu44g1yhm60ttwmj3w4q2-btjn3sbqv4bxcv2jnps6qw1pt 192.168.178.47:2377

このswarmにマネージャーを追加するには、「docker swarm join-token manager」を実行し、指示に従います。

このコマンドは、他のマシンをワーカーとしてスウォームに参加させるためにどうすればよいかも出力してくれます。この提案に従って、他の2台のマシン(swarmnodeとswarmnode2)に接続し、上記のコマンドを実行してみましょう:

$ ssh swarmnode docker swarm join --token SWMTKN-1-04u5uytbvhf6j0vvosxgw00zk3x50mu44g1yhm60ttwmj3w4q2-btjn3sbqv4bxcv2jnps6qw1pt 192.168.178.47:2377
This node joined a swarm as a worker.
$ ssh swarmnode2 docker swarm join --token SWMTKN-1-04u5uytbvhf6j0vvosxgw00zk3x50mu44g1yhm60ttwmj3w4q2-btjn3sbqv4bxcv2jnps6qw1pt 192.168.178.47:2377
This node joined a swarm as a worker.

これで完了です!これで3台のマシンは接続され、1つのDocker swarmとして動作するようになりました。docker nodeコマンドでswarmの状態を確認することができます。なお、このコマンドはマネージャマシンで実行する必要があります:

$ docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION
uvhrq0mnx4bgqn7tn6qgbkv1h swarmnode Ready Active 20.10.3
n0kcmxqxllqx1h4t23sz85z7l swarmnode2 Ready Active 20.10.3
d0tcyemebrcgxjrq6xihz3zoe * ubuntu Ready Active Leader 19.03.6

スウォームには3つのノードがあり、すべて動作可能な状態になっていることがわかります。そのうちの1台(ホスト名がubuntuのもの)がスウォームのリーダーとなっています。

分散型テスト実行

Docker swarmへのタスクのデプロイはdocker serviceコマンドで行い、swarmのマネージャで実行する必要があります。少なくとも、イメージの名前(スウォーム内の全ノードがアクセス可能であること)は与えられます。次にマネージャは、システム負荷などを考慮した上でスウォーム内の適切なマシンを選択し、そのマシン上でコンテナを起動します。特に指定がない限り、マネージャはコンテナが終了した場合に自動的に再起動されるようにします。これは、データベースやウェブサーバなど、常に利用可能であるべきサービスにとって有用です。

私たちのユースケースは少し異なります:

  • コンテナを再起動させる必要はありません。コンテナは起動してテストを実行した後、終了する必要があります。
  • addressbook-squishが実行するデフォルトのコマンドは、単に仮想ディスプレイ(Xvfb)を起動させるだけです。実際にテストを実行するわけではありません。これについては、以前のブログ記事をご参照ください。

幸いなことに、これらの要件はdocker serviceコマンドに追加の引数を渡すことで簡単に対処できます。ここでは、コンテナが自動的に停止するように、別の再起動動作(none)を指定することにします。また、コンテナ内で実行するカスタムコマンドを指定します。addressbook-squishコンテナを丁寧に準備したおかげで、Squishがインストールされている場所とテストスイートが保存されている場所が正確に分かっています。この知識を活用することで、以下のように必要なテストケースだけを実行するコマンドラインを作成することができます:

$ docker service create \ --name=tst_adding \
--restart-condition none \
192.168.178.47:5000/addressbook-squish \
/root/squish/bin/squishrunner --testsuite /tmp/suite_js --testcase tst_adding --local --reportgen testcenter,http://testcenter/project/addressbook?token=ABCDEF

個別の解説は以下になります:

  • docker service createは、Docker swarmにディスパッチするための「サービス」を作成するために使用します。
  • --name=tst_addingは、サービスにtst_addingという名前を割り当てています。これは、後でスウォームの状態を調べて、すべてのノードが何に取り組んでいるのかを確認するときに便利です。
  • --restart-condition none は、サービスの再起動動作を変更し、自動的に再起動する代わりに、単に停止するようにしています。これはまさに私たちが必要としている動作です。「fire and forget」コンテナは、テストケースの期間中実行され、最後に停止します。
  • 192.168.78.47:5000/addressbook-squish は、コンテナの作成に使用するイメージの名前です。これにはプライベートDockerレジストリのアドレスが含まれていることに注意してください。すべてのワーカーノードがこれにアクセスできることが重要です。
  • 最後に、以下のコマンド

  /root/squish/bin/squishrunner --testsuite /tmp/suite_js --testcase tst_adding --local --reportgen testcenter,http://testcenter/project/addressbook?token=ABCDEF

は、新しく作成されたコンテナで実行されます。Squishが/root/squishにインストールされ、実行されるテストケースが/tmp/suite_js/tst_addingに保存されていることを前提としています。Squishのコマンドラインインターフェイスを利用し、テストの実行をトリガーします。テストレポートは、URL http://testcenter/project/addressbook?token=ABCDEF を処理する Squish Test Center インスタンスに送信されます。

私たちのsuite_jsテストスイートには、1つのケースだけでなく、複数のケースが含まれています。異なるテストケース(tst_general、tst_adding、tst_adding_data)に対して上記のコマンドを3回実行すると、3つのコンテナが起動し、我々の場合、それらはスウォーム内の異なるノードによって処理されます。

docker node psコマンドでノードの状態を検査することができます:

ID                  NAME                    IMAGE                                           NODE                DESIRED STATE       CURRENT STATE            ERROR               PORTS
gctzag2iqfzk \_ tst_adding_data.1 192.168.178.47:5000/addressbook-squish:latest swarmnode Running Running 11 seconds ago
06xn0zrx90ts tst_general.1 192.168.178.47:5000/addressbook-squish:latest ubuntu Running Running 18 seconds ago
yntt9w7x18zg \_ tst_adding.1 192.168.178.47:5000/addressbook-squish:latest swarmnode2 Shutdown Complete 3 seconds ago

上記を確認すると、3つのテストケースが同時に実行されていることがわかります。実際、そのうちの1つ(tst_adding)はわずか3秒前に終了しています。この結果は、Squish Test CenterのWebインターフェイスで確認することができます。

ここから拡張するには

スウォームをセットアップした後は、テストが実行される場所の正確な詳細を気にすることなく、追加のテスト実行をスウォームにアサインすることができます。また、コンテナ化によって、すべてのテスト実行が再現可能であり、システムの残りの部分に影響を与えることなく分離されて実行されることを保証することができます。

ここまでで構築したシステムを拡張する方法はいくつもあります:

  • もちろん、追加のノードをswarmに追加することも可能です。docker swarm joinコマンドをさらに数台のコンピュータで実行するだけで、テスト実行に利用できるコンピューティングパワーの量を簡単に増やすことができます。
  • すべてのテストを同時に実行することは素晴らしいことですが、もっと良い方法があります。アプリケーションのソースコードに変更があった場合、すべてのテストを実行する必要はないかもしれません。コードカバレッジツールCocoのようなツールを使ってソースコードの違いを分析し、変更によって影響を受けるテストケースを計算することができます。これにより、実行するテストの数をさらに減らすことができます。
  • Dockerコンテナをオーケストレーションするための、より複雑ではあるものの、より洗練されたシステムとして、Kubernetes(英語)があります。Googleで開発されたこのシステムは、分散システムのためのオールインワンフレームワークです。新しい用語(ポッド、デプロイメント、マイクロサービス)を導入しており、Docker Swarmよりも稼働させるのが難しいという課題はありますが、多くのログ取得サービスや監視サービスをサポートしており、コンテナオーケストレーションツールの中でも非常に人気があります。

 

お問い合わせ

SquishをはじめとするQtのQA(品質保証)ツールにご興味がおありの方は、Qt JapanのEメールアドレス:japan@qt.ioまでお気軽にご連絡ください。概要のご説明から詳細な技術的相談、また無料のツールトライアルのご案内もいたしております。


Blog Topics:

Comments