こんにちは この記事は、モバイルプラットフォームSailfish OSの開発に関する一連の記事の続きです。 今回は、モバイルデバイス用に作成されたアプリケーションのQMLコンポーネントのテストを整理する方法について説明します。 コードを記述してから実際のデバイスでテストを実行するまでのすべての手順を検討してください。
テストアプリケーション
例として、単純なカウンターアプリケーションを検討します。 現在のカウンター値を表示するフィールドが含まれています。 追加ボタンをクリックすると、カウンターの値が1つ増えます。 プルアウトメニューには、カウンターをゼロにリセットする項目があります。
アプリケーションのセットアップ
テストを記述するために、
QtTestフレームワークが
使用され 、具体的には
TestCase型のQMLオブジェクトが
使用されます。 これを使用して、画面をクリックするアクションを実行し、実際の値に準拠しているかどうかの期待値を確認できます。 執筆時点では、Sailfish SDKはQtバージョン5.2を使用しているため、ドキュメントに記載されているすべてのメソッドが利用できるわけではありません。
アプリケーションでQtTestフレームワークを使用するには、PkgConfigBRでのアセンブリ用に* .yamlファイルに依存関係を追加する必要があります。
- Qt5Test
。 次に、テストのインストールパスを次のように* .proファイルに登録する必要があります。
tests.files = tests/* tests.path = /usr/share/counter-application/tests INSTALLS += tests OTHER_FILES += tests/*
この例では、
tests.files変数に、プロジェクト内のテストを含むディレクトリのアドレスと、これらのテストがデバイスにインストールされるパスである
tests.pathが書き込まれます。
テスト実装
テストファイルは、
tst_プレフィックスで始まる必要があります。 テストはQMLで記述され、ルート要素は
TestCase型のオブジェクトであり、その内部で関数が宣言されます。 プレフィックス
test_で始まる関数はテストと見なされ、フレームワークによって起動されます。 たとえば、簡単なテストを作成して、
tst_counter.qmlファイルに
配置します。
import QtQuick 2.0 import Sailfish.Silica 1.0 import QtTest 1.0 TestCase { function test_addition() { compare(2 + 2, 4); } }
アプリケーションのQMLコンポーネントをテストするには、
qml / project-nameファイルで定義されているメイン要素を使用する必要があります。 キャメルケース形式の名前を持つファイルで定義されたアイテムにのみアクセスできることが重要です。 したがって、適切な形式の名前(
qml / ProjectName )で補助ファイルが作成され、
そこにqml / projectNameの内容全体
が転送されます。 前と同様に、アプリケーションを起動するために、
ProjectNameという要素がソースファイルに挿入されます。 この場合、
counter-application.qmlファイルの内容は新しい
CounterApplication.qmlファイルに転送されます。
counter-application.qmlファイルには、次のものが残されています。
CounterApplication { }
次に、アプリケーションのロード後にテストを実行するようにTestCaseを構成する必要があります。 このオブジェクトのプロパティを考慮してください:
- completed :bool-テストスイートの完了後にtrueに設定
- name :string-レポート出力用のテストスイートの名前
- オプション :bool- trueフラグが設定されている場合、テストはスキップされます(デフォルトではfalse )
- running :bool-テストスイートが実行されている場合はtrueを含む
- when :bool-テストスイートを実行するにはtrueに設定する必要があります(デフォルトはtrue )
- windowShown :bool- TestCaseを含むコンポーネントが表示された場合はtrueが含まれます
アプリケーションのQMLコンポーネントをテストするには、アプリケーションを記述するオブジェクト内に
TestCaseを配置する必要があります。 以前、別のファイルでオブジェクトを選択し、他のファイルで使用できます。 アプリケーションウィンドウが表示されたときにのみテストを実行する
には 、
whenプロパティと
windowShownプロパティを使用する必要があります。 また、テストスイートの
名前をnameプロパティに設定します。 テストでは、次のようになります。
CounterApplication { TestCase { name: "Counter tests" when: windowShown function test_addition() { compare(2 + 2, 4); } } }
これで、CounterApplicationオブジェクトがテストで使用可能になり、表示されたビューとやり取りできるようになりました。
QtTestフレームワークは、標準のQtコンポーネントと対話するためのメソッドを提供します。 残念ながら、Sailfish Silicaのコンポーネントは標準ではないため、それらを自分で操作するためのメソッドを記述する必要があります。 この問題を解決するために、Sailfishコンポーネントとやり取りするためのメソッドを追加する
TestCaseクラスを拡張します。 ルート要素が
TestCaseオブジェクトである
SailfishTestCase.qmlファイルを作成します。 この
TestCase内に、テスト内で使用するメソッドを追加します。 テストファイルの後半では、
TestCaseオブジェクトの代わりに
SailfishTestCaseオブジェクトを使用し、追加されたメソッドを使用します。
最初に、ビューによって表示される要素を見つける必要があります。 QMLは
idプロパティを使用してビュー要素にアクセスしますが、外部からはアクセスできません。 したがって、表示されたフォームで検索する必要がある要素については、
objectNameプロパティの値を設定し、それを使用して要素を探します。 検索するために、再帰的な下降を深さに整理し、オブジェクトのプロパティの値を探して等しいかどうかを確認できます。 特定のプロパティが特定の値を持つ要素を見つけることができるメソッドが実装されました。
function findElementWithProperty(parent, propertyKey, propertyValue, exact, root) { if (parent.visible) { if (exact && parent[propertyKey] === propertyValue) return parent if (!exact && parent[propertyKey] !== undefined && parent[propertyKey].search(propertyValue) !== -1) { return parent } } if (parent.children !== undefined && parent.visible) { for (var i = 0; i < parent.children.length; i++) { var element = findElementWithProperty(parent.children[i], propertyKey, propertyValue, exact, false); if (element !== undefined) return element; } } if (root) { fail("Element with property key '" + propertyKey + "' and value '" + propertyValue + "' not found"); } else { return undefined; } }
このメソッドのパラメーターは次のとおりです。
- parent-検索を開始する要素
- propertyKey-値がチェックされるプロパティ
- propertyValue-検出されるプロパティの値
- exact-求められた値と見つかった値の完全な一致が必要な場合はtrue、そうでない場合は値は部分文字列として検索されます
- root-現在の要素が開始の場合はtrue
このタイプの検索が最も需要があるため、
objectNameで要素を検索するための追加のメソッドが実装され
ました 。
function findElementWithObjectName(root, name) { return findElementWithProperty(root, "objectName", name, true, true); }
非標準のQtコンポーネントの顕著な例は、Sailfishアプリケーションで広く使用されているプルダウンメニューです。
TestCaseメソッドの中には、1回の呼び出しでそのようなメニューから項目を選択できるものはないため、この動作の次の実装が役立ちました。
function openPullDownMenu(element) { var x = element.width / 2; var startY = element.height / 10; mousePress(element, x, startY); for (var i = 1; i <= 5; i++) { mouseMove(element, x, startY * i); } mouseRelease(element, x, startY * i); } function clickElement(element) { mouseClick(element, element.width / 2, element.height / 2); wait(1000); } function clickPullDownElement(parent, name) { openPullDownMenu(parent); clickElement(findElementWithObjectName(parent, name)); }
openPullDownMenu(要素)メソッドを使用すると、ユーザーが行う方法でメニューをプルすることをシミュレートできます。最初に画面をクリックし、次にポインターを下に移動してメニューを開いて解放します。 パラメータは、この同じメニューを含むオブジェクトです。
clickElement(要素)メソッドも便利で、指定した要素をクリックして、クリックによって開始されたアクションが完了するまで1秒待つことができます。
上記のメソッドを組み合わせて、
clickPullDownElement(親、名前)メソッドを作成します。これにより、メソッドに渡された
親要素に含まれるメニューを開き、
nameパラメーターの値に等しい
objectNameプロパティの値を持つ要素をクリックできます。
これらのメソッドを使用して、アプリケーションのテストを作成できます。 1つ目はカウンターを2回インクリメントし、値が増加したことを確認します。 2番目のものは、カウンター値を増やしてリセットし、0になったことを確認します。
CounterApplication { SailfishTestCase { name: "Counter tests" when: windowShown function test_counterAdd() { var button = findElementWithObjectName(pageStack.currentPage, "addButton"); clickElement(button); clickElement(button); compare(findElementWithObjectName(pageStack.currentPage, "countText").text, "2"); } function test_counterReset() { var button = findElementWithObjectName(pageStack.currentPage, "addButton"); clickElement(button); clickElement(button); clickPullDownElement(pageStack.currentPage, "resetItem"); compare(findElementWithObjectName(pageStack.currentPage, "countText").text, "0"); } } }
アプリケーションはテスト実行の間に閉じず、データを消去しません。 テストの実行前後にデータを事前設定およびクリーンアップする責任は、開発者にあります。
TestCaseには、各テストの前後に呼び出される2つのメソッドがあります:
init() 、
cleanup() 。 これらのメソッドを使用して、アプリケーションの状態を元の状態に戻す必要があります。 すべてのテストが実行される前と後にそれぞれ呼び出される
initTestCase()および
cleanupTestCase()メソッドもあります。
この例では、各テストの後にカウンター値をリセットする必要があります。このため、次の
cleanup()メソッドの実装を追加します。
CounterApplication { SailfishTestCase { name: "Counter tests" when: windowShown ... function cleanup() { clickPullDownElement(pageStack.currentPage, "resetItem"); } } }
各テストを完了すると、プルアウトメニューのリセットボタンが押されます。
テストをビルドして実行する
テストを実行する前に、デバイス上でアプリケーションをビルドおよびデプロイする必要があります(物理デバイスとエミュレーターの両方が適しています)。 このプロセスは
、シリーズの
以前の記事で説明さ
れています 。 デバイスでテストを実行できるようにするには、次のコマンドを使用して2つのパッケージをインストールする必要があります。
pkcon install qt5-qtdeclarative-import-qttest pkcon install qt5-qtdeclarative-devel-tools
したがって、デバイスにQtTestフレームワークをインストールします。これにより、作成したテストを実行できます。
テストを実行するには、パラメーターとしてテストファイルへのパスを渡すqmltestrunnerユーティリティを使用します。 次のようになります。
/usr/lib/qt5/bin/qmltestrunner -input /usr/share/counter-application/tests/
その結果、次のことがわかります。
********* Start testing of qmltestrunner ********* Config: Using QtTest library 5.2.2, Qt 5.2.2 PASS : qmltestrunner::Counter tests::initTestCase() PASS : qmltestrunner::Counter tests::test_counterAdd() PASS : qmltestrunner::Counter tests::test_counterReset() PASS : qmltestrunner::Counter tests::cleanupTestCase() Totals: 4 passed, 0 failed, 0 skipped ********* Finished testing of qmltestrunner *********
追加した2つのテスト
test_counterAdd()および
test_counterReset()に加えて、
initTestCase()および
cleanupTestCase()メソッドの呼び出しが
表示されます 。
おわりに
その結果、Sailfish OSプラットフォームのアプリケーションでQMLコンポーネントをテストするためのテストを作成する方法を検討しました。 例として、単純なカウンターアプリケーションを考えました。そのソースは(テストとともに)
GitHubで入手でき
ます 。
技術的な問題
は、ロシア語を話すコミュニティのSailfish OSのTelegramまたは
VKontakteグループの
チャネルでも議論できます。
著者:セルゲイ・アヴェルキエフ