カスタム プラグインを使ったテストの並列実行
この例では、テストの並列実行をサポートするカスタム プラグインの作成方法を説明します。カスタム プラグインは、テスト スイートのパスと失敗のアサーション数をカウントします。TestRunner
を拡張するため、プラグインは matlab.unittest.plugins.TestRunnerPlugin
クラスの特定メソッドをオーバーライドします。さらに、テストの並列実行をサポートするため、プラグインは matlab.unittest.plugins.Parallelizable
インターフェイスをサブクラス化します。テストの並列実行には、Parallel Computing Toolbox™ が必要です。
プラグイン クラスの作成
現在のフォルダー内のファイルに、TestRunnerPlugin
クラスと Parallelizable
クラスの両方から継承する、並列化可能なプラグイン クラス AssertionCountingPlugin
を作成します。AssertionCountingPlugin
の完全なコードは、プラグイン クラス定義の概要を参照してください。
パスと失敗のアサーション数を追跡するには、properties
ブロック内で 4 つの読み取り専用プロパティを定義します。現在の並列プールの MATLAB® ワーカーはそれぞれ NumPassingAssertions
と NumFailingAssertions
を使用して、配列 TestSuite
の一部を実行するときに、パスと失敗のアサーション数を追跡します。MATLAB クライアントは FinalizedNumPassingAssertions
と FinalizedNumFailingAssertions
を使用して異なるワーカーからの結果を集計し、テスト セッションの最後にパスと失敗のアサーション数の合計をレポートします。
properties (SetAccess = private) NumPassingAssertions NumFailingAssertions FinalizedNumPassingAssertions FinalizedNumFailingAssertions end
テスト セッションの実行の拡張
配列 TestSuite
全体の実行を拡張するには、protected
アクセスが指定された methods
ブロック内で TestRunnerPlugin
の runSession
メソッドをオーバーライドします。テスト フレームワークはこのメソッドをクライアント上で 1 回評価します。
methods (Access = protected) function runSession(plugin, pluginData) suiteSize = numel(pluginData.TestSuite); fprintf('## Running a total of %d tests\n\n', suiteSize); plugin.FinalizedNumPassingAssertions = 0; plugin.FinalizedNumFailingAssertions = 0; runSession@matlab.unittest.plugins.TestRunnerPlugin(plugin, pluginData); fprintf('## Done running tests\n') plugin.printAssertionSummary() end end
runSession
は Test
要素数の合計についての情報を表示し、プラグインでテキスト出力の生成に使用されるプロパティを初期化し、スーパークラス メソッドを呼び出してテスト実行全体をトリガーします。フレームワークがスーパークラス メソッドの評価を完了すると、runSession
は、補助メソッド printAssertionSummary
(補助メソッドの定義を参照) を呼び出すことによりアサーション カウントの概要を表示します。
共有テスト フィクスチャと TestCase インスタンスの作成の拡張
アサーションをカウントするためにイベント AssertionPassed
および AssertionFailed
にリスナーを追加します。これらのリスナーを追加するには、テスト フレームワークで使用されるメソッドを拡張してテスト内容を作成します。テスト内容には、Test
要素ごとの TestCase
インスタンス、TestClassSetup
および TestClassTeardown
メソッド ブロックに対するクラス レベルの TestCase
インスタンス、および TestCase
クラスが属性 SharedTestFixtures
をもつ場合に使用する Fixture
インスタンスが含まれます。
作成メソッドをオーバーライドする際に、対応するスーパークラスを呼び出します。作成メソッドは、テスト フレームワークがそれぞれのコンテキストに対して作成する内容を返します。これらのいずれかのメソッドを incrementPassingAssertionsCount
および incrementFailingAssertionsCount
補助メソッドを使用して実装する場合、プラグインで必要なリスナーを、返された Fixture
または TestCase
インスタンスに追加します。
protected
アクセスが指定された methods
ブロックにこれらの作成メソッドを追加します。
methods (Access = protected) function fixture = createSharedTestFixture(plugin, pluginData) fixture = createSharedTestFixture@... matlab.unittest.plugins.TestRunnerPlugin(plugin, pluginData); fixture.addlistener('AssertionPassed', ... @(~,~)plugin.incrementPassingAssertionsCount); fixture.addlistener('AssertionFailed', ... @(~,~)plugin.incrementFailingAssertionsCount); end function testCase = createTestClassInstance(plugin, pluginData) testCase = createTestClassInstance@... matlab.unittest.plugins.TestRunnerPlugin(plugin, pluginData); testCase.addlistener('AssertionPassed', ... @(~,~)plugin.incrementPassingAssertionsCount); testCase.addlistener('AssertionFailed', ... @(~,~)plugin.incrementFailingAssertionsCount); end function testCase = createTestMethodInstance(plugin, pluginData) testCase = createTestMethodInstance@... matlab.unittest.plugins.TestRunnerPlugin(plugin, pluginData); testCase.addlistener('AssertionPassed', ... @(~,~)plugin.incrementPassingAssertionsCount); testCase.addlistener('AssertionFailed', ... @(~,~)plugin.incrementFailingAssertionsCount); end end
テスト スイートの一部の実行の拡張
テスト フレームワークは配列 TestSuite
全体を別々のグループに分割し、それらをワーカーに割り当てて処理します。各ワーカーは、テスト スイートの部分を 1 つ以上実行できます。ワーカーの動作をカスタマイズするには、protected
アクセスが指定された methods
ブロック内で TestRunnerPlugin
の runTestSuite
メソッドをオーバーライドします。
TestRunner
を拡張し、ワーカーが実行する各テスト グループの識別子を、グループ内の Test
要素数とともに表示します。さらに、パスと失敗のアサーション数をバッファーに格納し、確定した結果を生成するためにクライアントでこれらの値を取得できるようにします。すべてのプラグイン メソッドと同様に、runTestSuite
メソッドでは対応するスーパークラス メソッドを適切な時点で呼び出す必要があります。この場合は、プロパティの初期化後かつワーカー データの保存前に、スーパークラス メソッドを呼び出します。テスト フレームワークは、ワーカーの runTestSuite
をテスト スイートの部分の数と同数回評価します。
methods (Access = protected) function runTestSuite(plugin, pluginData) suiteSize = numel(pluginData.TestSuite); groupNumber = pluginData.Group; fprintf('### Running a total of %d tests in group %d\n', ... suiteSize, groupNumber); plugin.NumPassingAssertions = 0; plugin.NumFailingAssertions = 0; runTestSuite@matlab.unittest.plugins.TestRunnerPlugin(... plugin, pluginData); assertionStruct = struct('Passing', plugin.NumPassingAssertions, ... 'Failing', plugin.NumFailingAssertions); plugin.storeIn(pluginData.CommunicationBuffer, assertionStruct); end end
テスト特有のデータを保存するため、runTestSuite
の実装には Parallelizable
インターフェイスの storeIn
メソッドの呼び出しが含まれています。ワーカーがクライアントにレポートしなければならない場合は、storeIn
を retrieveFrom
とともに使用します。この例では、スーパークラス メソッドから戻った後、NumPassingAssertions
と NumFailingAssertions
にはテストのグループに対応するパスと失敗のアサーション数が含まれています。storeIn
はワーカー データを唯一の入力引数として受け入れるため、assertionStruct
は 2 つのフィールドを使用してアサーションのカウント数をグループ化します。
テスト スイートの確定部分のレポートの拡張
reportFinalizedSuite
を拡張して、テスト スイートの確定した各部分のテスト データを取得することにより、アサーションのカウント数を集計します。テスト スイートの部分の保存された assertionStruct
を取得するには、reportFinalizedSuite
のスコープ内で retrieveFrom
メソッドを呼び出します。フィールドの値を対応するクラス プロパティに追加し、スーパークラス メソッドを呼び出します。テスト フレームワークは、クライアント上でこのメソッドをテスト スイートの部分の数と同数回評価します。
methods (Access = protected) function reportFinalizedSuite(plugin, pluginData) assertionStruct = plugin.retrieveFrom(pluginData.CommunicationBuffer); plugin.FinalizedNumPassingAssertions = ... plugin.FinalizedNumPassingAssertions + assertionStruct.Passing; plugin.FinalizedNumFailingAssertions = ... plugin.FinalizedNumFailingAssertions + assertionStruct.Failing; reportFinalizedSuite@matlab.unittest.plugins.TestRunnerPlugin(... plugin, pluginData); end end
補助メソッドの定義
private
アクセスが指定された methods
ブロックで、3 つの補助メソッドを定義します。これらのメソッドは、実行中のテスト スイートの各部分内でパスまたは失敗のアサーション数をインクリメントし、アサーション カウントの概要を出力します。
methods (Access = private) function incrementPassingAssertionsCount(plugin) plugin.NumPassingAssertions = plugin.NumPassingAssertions + 1; end function incrementFailingAssertionsCount(plugin) plugin.NumFailingAssertions = plugin.NumFailingAssertions + 1; end function printAssertionSummary(plugin) fprintf('%s\n', repmat('_', 1, 30)) fprintf('Total Assertions: %d\n', plugin.FinalizedNumPassingAssertions + ... plugin.FinalizedNumFailingAssertions) fprintf('\t%d Passed, %d Failed\n', plugin.FinalizedNumPassingAssertions, ... plugin.FinalizedNumFailingAssertions) end end
プラグイン クラス定義の概要
以下のコードは、AssertionCountingPlugin
の完全な内容を提示します。
classdef AssertionCountingPlugin < ... matlab.unittest.plugins.TestRunnerPlugin & ... matlab.unittest.plugins.Parallelizable properties (SetAccess = private) NumPassingAssertions NumFailingAssertions FinalizedNumPassingAssertions FinalizedNumFailingAssertions end methods (Access = protected) function runSession(plugin, pluginData) suiteSize = numel(pluginData.TestSuite); fprintf('## Running a total of %d tests\n\n', suiteSize); plugin.FinalizedNumPassingAssertions = 0; plugin.FinalizedNumFailingAssertions = 0; runSession@matlab.unittest.plugins.TestRunnerPlugin(plugin, pluginData); fprintf('## Done running tests\n') plugin.printAssertionSummary() end function fixture = createSharedTestFixture(plugin, pluginData) fixture = createSharedTestFixture@... matlab.unittest.plugins.TestRunnerPlugin(plugin, pluginData); fixture.addlistener('AssertionPassed', ... @(~,~)plugin.incrementPassingAssertionsCount); fixture.addlistener('AssertionFailed', ... @(~,~)plugin.incrementFailingAssertionsCount); end function testCase = createTestClassInstance(plugin, pluginData) testCase = createTestClassInstance@... matlab.unittest.plugins.TestRunnerPlugin(plugin, pluginData); testCase.addlistener('AssertionPassed', ... @(~,~)plugin.incrementPassingAssertionsCount); testCase.addlistener('AssertionFailed', ... @(~,~)plugin.incrementFailingAssertionsCount); end function testCase = createTestMethodInstance(plugin, pluginData) testCase = createTestMethodInstance@... matlab.unittest.plugins.TestRunnerPlugin(plugin, pluginData); testCase.addlistener('AssertionPassed', ... @(~,~)plugin.incrementPassingAssertionsCount); testCase.addlistener('AssertionFailed', ... @(~,~)plugin.incrementFailingAssertionsCount); end function runTestSuite(plugin, pluginData) suiteSize = numel(pluginData.TestSuite); groupNumber = pluginData.Group; fprintf('### Running a total of %d tests in group %d\n', ... suiteSize, groupNumber); plugin.NumPassingAssertions = 0; plugin.NumFailingAssertions = 0; runTestSuite@matlab.unittest.plugins.TestRunnerPlugin(... plugin, pluginData); assertionStruct = struct('Passing', plugin.NumPassingAssertions, ... 'Failing', plugin.NumFailingAssertions); plugin.storeIn(pluginData.CommunicationBuffer, assertionStruct); end function reportFinalizedSuite(plugin, pluginData) assertionStruct = plugin.retrieveFrom(pluginData.CommunicationBuffer); plugin.FinalizedNumPassingAssertions = ... plugin.FinalizedNumPassingAssertions + assertionStruct.Passing; plugin.FinalizedNumFailingAssertions = ... plugin.FinalizedNumFailingAssertions + assertionStruct.Failing; reportFinalizedSuite@matlab.unittest.plugins.TestRunnerPlugin(... plugin, pluginData); end end methods (Access = private) function incrementPassingAssertionsCount(plugin) plugin.NumPassingAssertions = plugin.NumPassingAssertions + 1; end function incrementFailingAssertionsCount(plugin) plugin.NumFailingAssertions = plugin.NumFailingAssertions + 1; end function printAssertionSummary(plugin) fprintf('%s\n', repmat('_', 1, 30)) fprintf('Total Assertions: %d\n', plugin.FinalizedNumPassingAssertions + ... plugin.FinalizedNumFailingAssertions) fprintf('\t%d Passed, %d Failed\n', plugin.FinalizedNumPassingAssertions, ... plugin.FinalizedNumFailingAssertions) end end end
サンプル テスト クラスの作成
現在のフォルダーに、以下のパラメーター化されたテスト クラスを含む ExampleTest.m
という名前のファイルを作成します。このクラスは 300 個の Test
要素となり、そのうち 100 個は、1 ~ 10 の整数の疑似乱数を比較するアサーション テストです。
classdef ExampleTest < matlab.unittest.TestCase properties (TestParameter) num1 = repmat({@()randi(10)}, 1, 10); num2 = repmat({@()randi(10)}, 1, 10); end methods(Test) function testAssert(testCase, num1, num2) testCase.assertNotEqual(num1(), num2()) end function testVerify(testCase, num1, num2) testCase.verifyNotEqual(num1(), num2()) end function testAssume(testCase, num1, num2) testCase.assumeNotEqual(num1(), num2()) end end end
TestRunner にプラグインを追加してテストを実行
コマンド プロンプトで ExampleTest
クラスからテスト スイートを作成します。
import matlab.unittest.TestSuite import matlab.unittest.TestRunner suite = TestSuite.fromClass(?ExampleTest);
プラグインなしで TestRunner
インスタンスを作成します。このコードはメッセージを表示しないランナーを作成し、インストールされたプラグインの制御を可能にします。
runner = TestRunner.withNoPlugins;
AssertionCountingPlugin
をランナーに追加して、テストを並列で実行します。ランナーで run
メソッドを呼び出すと、同じテストを逐次モードで実行することもできます。
runner.addPlugin(AssertionCountingPlugin) result = runner.runInParallel(suite);
Starting parallel pool (parpool) using the 'local' profile ... Connected to the parallel pool (number of workers: 6). ## Running a total of 300 tests Split tests into 18 groups and running them on 6 workers. ---------------- Finished Group 6 ---------------- ### Running a total of 18 tests in group 6 ---------------- Finished Group 1 ---------------- ### Running a total of 20 tests in group 1 ---------------- Finished Group 2 ---------------- ### Running a total of 20 tests in group 2 ---------------- Finished Group 3 ---------------- ### Running a total of 19 tests in group 3 ---------------- Finished Group 4 ---------------- ### Running a total of 19 tests in group 4 ---------------- Finished Group 5 ---------------- ### Running a total of 18 tests in group 5 ---------------- Finished Group 7 ---------------- ### Running a total of 18 tests in group 7 ---------------- Finished Group 8 ---------------- ### Running a total of 17 tests in group 8 ---------------- Finished Group 9 ---------------- ### Running a total of 17 tests in group 9 ----------------- Finished Group 10 ----------------- ### Running a total of 17 tests in group 10 ----------------- Finished Group 11 ----------------- ### Running a total of 16 tests in group 11 ----------------- Finished Group 12 ----------------- ### Running a total of 16 tests in group 12 ----------------- Finished Group 15 ----------------- ### Running a total of 15 tests in group 15 ----------------- Finished Group 14 ----------------- ### Running a total of 15 tests in group 14 ----------------- Finished Group 17 ----------------- ### Running a total of 14 tests in group 17 ----------------- Finished Group 16 ----------------- ### Running a total of 14 tests in group 16 ----------------- Finished Group 13 ----------------- ### Running a total of 15 tests in group 13 ----------------- Finished Group 18 ----------------- ### Running a total of 12 tests in group 18 ## Done running tests ______________________________ Total Assertions: 100 88 Passed, 12 Failed
参考
matlab.unittest.plugins.TestRunnerPlugin
| matlab.unittest.TestCase
| matlab.unittest.TestRunner
| matlab.unittest.fixtures.Fixture
| matlab.unittest.plugins.Parallelizable
| matlab.unittest.TestSuite
| addlistener
| runInParallel
| matlab.unittest.TestResult