Sailfish OSの開発:QMLコンポーネントのテスト

こんにちは この記事は、モバイルプラットフォーム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を構成する必要があります。 このオブジェクトのプロパティを考慮してください:





アプリケーションの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; } }
      
      





このメソッドのパラメーターは次のとおりです。



  1. parent-検索を開始する要素
  2. propertyKey-値がチェックされるプロパティ
  3. propertyValue-検出されるプロパティの値
  4. exact-求められた値と見つかった値の完全な一致が必要な場合はtrue、そうでない場合は値は部分文字列として検索されます
  5. 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秒待つことができます。



上記のメソッドを組み合わせて、 clickPul​​lDownElement(親、名前)メソッドを作成します。これにより、メソッドに渡された要素に含まれるメニューを開き、 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グループチャネルでも議論できます。



著者:セルゲイ・アヴェルキエフ



All Articles