要件に基づくテストを使用した MATLAB アルゴリズムの検証
この例では、関数とテストの MATLAB® コード行から要件への検証リンクを作成して MATLAB アルゴリズムを検証する方法を示します。この例では、グラフ上の 2 つのノード間の最短経路を計算するアルゴリズムを含むプロジェクトを使用します。
ShortestPath
プロジェクトを開きます。
openProject("ShortestPath");
プロジェクトのアーティファクトの確認
プロジェクトには以下が含まれます。
機能要件とテスト要件の要件セット (
requirements
フォルダーに格納)MATLAB アルゴリズム (
src
フォルダーに格納)MATLAB ユニット テスト (
tests
フォルダーに格納)MATLAB コード行から要件へのリンク (
.slmx
ファイルとしてsrc
フォルダーとtests
フォルダーに格納)プロジェクトの解析を自動化するためのスクリプト (
scripts
フォルダーに格納)
機能要件セットを開く
shortest_path_func_reqs
要件セットは、関数 shortest_path
に必要な機能的な動作を取得したものです。定格の動作について記述した要件と、関数への入力が有効でない場合など、無効な条件における想定される動作について記述した要件があります。[要件エディター] で要件セットを開きます。
funcReqs = slreq.open("shortest_path_func_reqs");
最短経路関数の使用
関数 shortest_path
は、関数への入力の有効性をテストしてから、ダイクストラ アルゴリズムを使用してグラフ上の 2 つのノード間の最短経路にあるエッジの数を計算します。関数への入力は、グラフ、開始ノード、終了ノードを表す隣接行列です。たとえば、6 つのノードをもつグラフを表す次の隣接行列について考えてみます。
A = [0 1 0 0 1 0; 1 0 1 0 0 0; 0 1 0 1 0 0; 0 0 1 0 1 1; 1 0 0 1 0 0; 0 0 0 1 0 0];
行列からグラフを作成してプロットします。
G = graph(A); plot(G,EdgeLabel=G.Edges.Weight)
ノード 1 とノード 6 の間の最短経路にあるエッジの数を計算します。
pathLength = shortest_path(A,1,6)
pathLength = 3
テスト要件セットを開く
shortest_path_tests_reqs
要件セットには、テスト ケースでテストする必要がある機能的な動作について記述したテスト要件が含まれています。テスト要件は機能要件から派生したものです。定格の動作についてのテスト要件と無効な条件についてのテスト要件があります。[要件エディター] で要件セットを開きます。
testReqs = slreq.open("shortest_path_tests_reqs");
graph_unit_tests
のクラスベースの MATLAB ユニット テストには、shortest_path_tests_reqs
で記述されているテスト ケースが実装されています。このクラスには、shortest_path_tests_reqs
からのテスト要件に基づくテスト メソッドが含まれています。また、テスト ケースで検定メソッドとして使用する verify_path_length
メソッドも含まれています。これを使用して、想定される結果と実際の結果が等しいことが検証されます。さらに、テスト ケースの隣接行列を作成する静的メソッドも含まれています。
検証ステータスの表示
検証ステータスを確認するには、[要件エディター] のツールストリップで [ビュー] セクションの [列] をクリックし、[検証ステータス] を選択します。機能要件の 3 つとテスト要件の 1 つに検証リンクがありません。各要件の検証ステータスが黄色であり、リンクされたテストが実行されていないことを示しています。
runTests
メソッドを使用してテストを実行し、要件セットの検証ステータスを更新します。
status1 = runTests(funcReqs);
Running graph_unit_tests .......... .. Done graph_unit_tests __________
status2 = runTests(testReqs);
Running graph_unit_tests .......... ... Done graph_unit_tests __________
検証ステータスが緑色になり、リンクされたテストにパスしたことが示されます。ただし、テストへのリンクがない要件がいくつかあります。
プロジェクトのトレーサビリティ ギャップの特定
機能要件とテスト要件は shortest_path
ファイルと graph_unit_tests
ファイルのコード行にリンクされていますが、トレーサビリティが完全ではありません。トレーサビリティ マトリクスを使用して、テストにリンクされていない要件を特定し、リンクを作成して要件を完全にトレースできるようにします。
トレーサビリティ マトリクスを使用した欠損リンクの特定
要件を上にし、ユニット テストを左にして、両方の要件セットのトレーサビリティ マトリクスを作成します。トレーサビリティ マトリクスの詳細については、トレーサビリティ マトリクスを使用した要件リンクの追跡を参照してください。
mtxOpts = slreq.getTraceabilityMatrixOptions; mtxOpts.topArtifacts = {'shortest_path_func_reqs.slreqx','shortest_path_tests_reqs.slreqx'}; mtxOpts.leftArtifacts = {'graph_unit_tests'}; slreq.generateTraceabilityMatrix(mtxOpts)
[フィルター パネル] の [上] セクションで、次のようにクリックして、テストにリンクされていない機能要件だけを表示するようにマトリクスをフィルター処理します。
[上]、[リンク]、[欠損リンク]
[上]、[タイプ]、[機能要件]
[左] セクションで、次のようにクリックして、graph_unit_tests
ファイルのテスト関数だけを表示します。
[左]、[タイプ]、[関数]
[左]、[属性]、[テスト]
ツールストリップで [欠損リンクの強調表示] をクリックします。
[トレーサビリティ マトリクス] ウィンドウに、検証リンクがない 3 つの機能要件と 1 つのテスト要件が表示されます。
要件の検証リンクの作成
テスト要件 2.1.3 の Test for a graph that is a tree
はテストにリンクされていません。ツリーのグラフでは、どの 2 つのノード間も 1 つの経路でのみ接続されています。
テスト ケース check_invalid_start_1
は、静的メソッド graph_straight_seq
を使用して隣接行列を作成することでツリー グラフをテストします。 graph_straight_seq
メソッドを使用してツリー グラフを表示します。
A = graph_unit_tests.graph_straight_seq; G = graph(A); plot(G,EdgeLabel=G.Edges.Weight)
前に生成したトレーサビリティ マトリクスを使用して、要件 Test for a graph that is a tree
から check_invalid_start_1
テスト ケースへのリンクを作成します。
slreq.generateTraceabilityMatrix(mtxOpts)
要件とテストに対応するセルをクリックし、[作成] を選択します。[リンクの作成] ダイアログ ボックスで、[作成] をクリックします。
テスト要件にリンクされたテストを実行して、[要件エディター] の検証ステータスを更新します。check_invalid_start_1
テストで要件 Test for a graph that is a tree
が検証されます。
status3 = runTests(testReqs);
Running graph_unit_tests .......... ... Done graph_unit_tests __________
さらに、次の 3 つの機能要件にテストへのリンクがありません。
要件 2.2.1:
Returns -9 for invalid adjacency matrices
要件 2.2.2:
Returns -19 if the start node is encoded incorrectly
要件 2.2.3:
Returns -29 if end node is encoded incorrectly
これらの要件にはトレーサビリティ ギャップがあります。これらの要件を検証するテストはないため、このギャップをテストへのリンクを作成して埋めることはできません。
カバレッジ ギャップとトレーサビリティ ギャップを修正するためのテストの作成
テストへのリンクがない 3 つの機能要件には、関数 shortest_path
のコード行へのリンクがありません。カバレッジを使用してテストを実行し、関数 shortest_path
のそれらのコード行がテストでカバーされているかどうかを判定します。
カバレッジを使用したテストの実行
RunTestsWithCoverage
スクリプトを使用して、関数とステートメントのカバレッジを使用してテストを実行し、それらのカバレッジをレポートで確認します。詳細については、MATLAB ソース コードのステートメントおよび関数のカバレッジ メトリクスの収集を参照してください。
RunTestsWithCoverage
Running graph_unit_tests .......... .... Done graph_unit_tests __________ MATLAB code coverage report has been saved to: C:\Users\ahoward\AppData\Local\Temp\tpc3b346ea_31dd_409d_be4c_5e787898bf8f\index.html
カバレッジ レポートを開きます。20 行目、25 行目、および 30 行目にあるエラー コードのステートメントがテストでカバーされていません。
それらのコード行のカバレッジ ギャップと要件 2.2.1、2.2.2、および 2.2.3 のトレーサビリティ ギャップが同じエラー コードを参照していることに注目してください。それらのコード行のテストを作成して要件へのリンクを作成すれば、カバレッジ ギャップとトレーサビリティ ギャップを同時に埋めることができます。
カバレッジの改善するための新しいテストの作成
テストのカバレッジを改善して要件 2.2.1、2.2.2、および 2.2.2 を検証するテストを作成します。graph_unit_tests
テスト ファイルを開きます。
open("graph_unit_tests.m");
次の関数で 3 つのエラー コードをテストします。このコードをコピーして graph_unit_tests
ファイルの 4 行目にあるテスト メソッドのセクションに貼り付け、ファイルを保存します。
function check_invalid_nonsquare(testCase) adjMatrix = zeros(2,3); startIdx = 1; endIdx = 1; expOut = -9; verify_path_length(testCase, adjMatrix, startIdx, endIdx, expOut, ... 'Graph is not square'); end function check_invalid_entry(testCase) adjMatrix = 2*ones(4,4); startIdx = 1; endIdx = 1; expOut = -9; verify_path_length(testCase, adjMatrix, startIdx, endIdx, expOut, ... 'Adjacency matrix is not valid'); end function check_invalid_noninteger_startnode(testCase) adjMatrix = zeros(4,4); startIdx = 1.2; endIdx = 1; expOut = -19; verify_path_length(testCase, adjMatrix, startIdx, endIdx, expOut, ... 'Start node is not an integer'); end function check_invalid_noninteger_endnode(testCase) adjMatrix = zeros(4,4); startIdx = 1; endIdx = 2.2; expOut = -29; verify_path_length(testCase, adjMatrix, startIdx, endIdx, expOut, ... 'End node is not an integer'); end
カバレッジを使用してテストを再度実行し、カバレッジ レポートを開きます。
RunTestsWithCoverage
Running graph_unit_tests .......... .... Done graph_unit_tests __________ MATLAB code coverage report has been saved to: C:\Users\ahoward\AppData\Local\Temp\tpd094de61_604e_45b4_8b53_767a6f4719cb\index.html
今度はエラー コードのステートメントがテストでカバーされています。
ただし、97 行目にテストでカバーされないステートメントがあります。これは、97 行目のステートメントをテストでカバーすることを必要とする条件で 87 行目の return
も実行されるためです。つまり、97 行目のステートメントには到達できず、デッド ロジックになります。
要件のトレーサビリティ ギャップの修正
トレーサビリティ マトリクスを再生成し、前と同じようにフィルターを適用してから、ツールストリップで [欠損リンクの強調表示] をクリックします。
slreq.generateTraceabilityMatrix(mtxOpts)
[上]、[リンク]、[欠損リンク]
[上]、[タイプ]、[機能要件]
[左]、[タイプ]、[関数]
[左]、[属性]、[テスト]
エラー コードの要件と新しいテストの間のリンクを作成します。
両方の要件セットにリンクされたテストを再実行して、[要件エディター] の検証ステータスを更新します。
status4 = runTests(funcReqs);
Running graph_unit_tests .......... .. Done graph_unit_tests __________
status5 = runTests(testReqs);
Running graph_unit_tests .......... ... Done graph_unit_tests __________
すべての要件にテストへのリンクがあり、すべてのテストにパスします。
生成されたコード内での要件のトレース
Embedded Coder® を使用して shortest_path
アルゴリズムからコードを生成し、生成されたコード内で要件をトレースできるようにする要件コメントを含めます。詳細については、Requirements Traceability for Code Generated from MATLAB Codeを参照してください。
コード構成オブジェクトを作成し、LIB ビルド タイプを使用してコードを生成します。
cfg = coder.config("lib","ecoder",true);
コード コンフィギュレーション パラメーターを有効にして、生成されたコードに要件コメントを含めます。
cfg.ReqsInCode = true;
coder.typeof
(MATLAB Coder)を使用して、最大サイズを 100 行 100 列とする可変サイズの double の配列と、生成されたコードで入力として使用する double のスカラーを定義します。
mtxType = coder.typeof(ones(100,100),[],1); scalarDblType = coder.typeof(1);
指定したコード コンフィギュレーション パラメーターと入力型を使用して、shortest_path
アルゴリズムから C コードを生成します。コード生成レポートを作成し、そのレポートを起動します。
codegen shortest_path -config cfg -args {mtxType, scalarDblType, scalarDblType} -launchreport
Code generation successful: View report
shortest_path.c
ファイルには、リンクされた要件、shortest_path.m
ファイルの絶対ファイル パス、リンクされたコード行からなる概要が記載されたコメントが含まれています。
参考
runTests
| codegen
(MATLAB Coder) | coder.runTest
(MATLAB Coder)