Main Content

このページの翻訳は最新ではありません。ここをクリックして、英語の最新版を参照してください。

カスタム プラグインを使ったテストの並列実行

この例では、テストの並列実行をサポートするカスタム プラグインの作成方法を説明します。カスタム プラグインは、テスト スイートのパスと失敗のアサーション数をカウントします。TestRunner を拡張するため、プラグインは matlab.unittest.plugins.TestRunnerPlugin クラスの特定メソッドをオーバーライドします。さらに、テストの並列実行をサポートするため、プラグインは matlab.unittest.plugins.Parallelizable インターフェイスをサブクラス化します。テストの並列実行には、Parallel Computing Toolbox™ が必要です。

プラグイン クラスの作成

現在のフォルダー内のファイルに、TestRunnerPlugin クラスと Parallelizable クラスの両方から継承する、並列化可能なプラグイン クラス AssertionCountingPlugin を作成します。AssertionCountingPlugin の完全なコードは、プラグイン クラス定義の概要を参照してください。

パスと失敗のアサーション数を追跡するには、properties ブロック内で 4 つの読み取り専用プロパティを定義します。現在の並列プールの MATLAB® ワーカーはそれぞれ NumPassingAssertionsNumFailingAssertions を使用して、配列 TestSuite の一部を実行するときに、パスと失敗のアサーション数を追跡します。MATLAB クライアントは FinalizedNumPassingAssertionsFinalizedNumFailingAssertions を使用して異なるワーカーからの結果を集計し、テスト セッションの最後にパスと失敗のアサーション数の合計をレポートします。

properties (SetAccess = private)
    NumPassingAssertions
    NumFailingAssertions
    FinalizedNumPassingAssertions
    FinalizedNumFailingAssertions
end

テスト セッションの実行の拡張

配列 TestSuite 全体の実行を拡張するには、protected アクセスが指定された methods ブロック内で TestRunnerPluginrunSession メソッドをオーバーライドします。テスト フレームワークはこのメソッドをクライアント上で 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

runSessionTest 要素数の合計についての情報を表示し、プラグインでテキスト出力の生成に使用されるプロパティを初期化し、スーパークラス メソッドを呼び出してテスト実行全体をトリガーします。フレームワークがスーパークラス メソッドの評価を完了すると、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 ブロック内で TestRunnerPluginrunTestSuite メソッドをオーバーライドします。

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 メソッドの呼び出しが含まれています。ワーカーがクライアントにレポートしなければならない場合は、storeInretrieveFrom とともに使用します。この例では、スーパークラス メソッドから戻った後、NumPassingAssertionsNumFailingAssertions にはテストのグループに対応するパスと失敗のアサーション数が含まれています。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

参考

| | | | | | | |

関連するトピック