Main Content

使用基于需求的测试验证 MATLAB 算法

此示例说明如何通过从需求的函数和测试中的 MATLAB 代码行创建验证链接来验证 MATLAB® 算法。此示例使用一个包含算法的工程来计算图上两个节点之间的最短路径。

打开 ShortestPath 工程。

openProject("ShortestPath");

检查工程工件

该工程包含:

  • 功能需求和测试需求的需求集,位于 requirements 文件夹中

  • 一个 MATLAB 算法,位于 src 文件夹中

  • MATLAB 单元测试,位于 tests 文件夹中

  • 从 MATLAB 代码行到需求的链接,存储在 srctests 文件夹的 .slmx 文件中

  • 用于自动执行工程分析的脚本,位于 scripts 文件夹中

打开功能需求集

shortest_path_func_reqs 需求集会捕获 shortest_path 函数所需的功能行为。需求描述了常规行为以及无效条件下(例如当函数的输入无效时)的预期行为。在需求编辑器中打开该需求集。

funcReqs = slreq.open("shortest_path_func_reqs");

使用最短路径函数

shortest_path 函数测试函数输入的有效性,然后使用戴克斯特拉算法计算图上两个节点之间最短路径中的边数。该函数的输入是表示图、开始节点和结束节点的邻接矩阵。例如,假设有以下表示具有六个节点的图的邻接矩阵。

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)

Figure contains an axes object. The axes object contains an object of type graphplot.

计算节点 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 方法,测试用例将其用作鉴定方法,以验证预期结果和实际结果是否相同。该类还包含为测试用例创建邻接矩阵的静态方法。

查看验证状态

要查看验证状态,请在需求编辑器工具条的视图部分中,点击 showColumns_16.png,然后选择验证状态。其中三项功能需求和一项测试需求缺少验证链接。每项需求的验证状态均为黄色,这表示链接的测试尚未运行。

missingVerification.png

使用 runTests 方法运行测试并更新需求集的验证状态。

status1 = runTests(funcReqs);
Running graph_unit_tests
.......... ..
Done graph_unit_tests
__________
status2 = runTests(testReqs);
Running graph_unit_tests
.......... ...
Done graph_unit_tests
__________

验证状态为绿色,表示链接的测试已通过。但是,有些需求没有指向测试的链接。

确定工程中的可追溯性缺失

功能需求和测试需求已链接到 shortest_pathgraph_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 文件中的测试函数:

  • 左侧 > 类型 > 函数

  • 左侧 > 属性 > 测试

点击工具条中的突出显示缺失链接

missingLinksTRMX.png

“可追溯性矩阵”窗口会显示缺少验证链接的三项功能需求和一项测试需求。

为需求创建验证链接

测试需求 2.1.3 Test for a graph that is a tree 未链接到测试。树是一种图,图中的任意两个节点只通过一条路径连接。

测试用例 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)

Figure contains an axes object. The axes object contains an object of type graphplot.

使用您之前生成的可追溯性矩阵,创建一个从 Test for a graph that is a tree 需求到 check_invalid_start_1 测试用例的链接。

slreq.generateTraceabilityMatrix(mtxOpts)

点击与该需求和测试相对应的单元格,然后选择创建。在“创建链接”对话框中,点击创建

createTestLink.png

通过运行链接到测试需求的测试,在需求编辑器中更新验证状态。check_invalid_start_1 测试用于验证 Test for a graph that is a tree 需求。

status3 = runTests(testReqs);
Running graph_unit_tests
.......... ...
Done graph_unit_tests
__________

此外,以下三项功能需求没有指向测试的链接:

  • 需求 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

这些需求存在可追溯性缺失。您无法通过创建指向测试的链接来填补该缺失,因为没有验证这些需求的测试。

通过编写测试来修复覆盖率和可追溯性缺失

没有测试链接的三项功能需求确实具有指向 shortest_path 函数中的代码行的链接。运行具有覆盖率的测试,以确定测试是否覆盖了 shortest_path 函数中的这些代码行。

运行具有覆盖率的测试

使用 RunTestsWithCoverage 脚本运行具有函数和语句覆盖率的测试,并在报告中查看覆盖率。有关详细信息,请参阅Collect Statement and Function Coverage Metrics for MATLAB Source Code

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 行中的错误代码语句。

missingCoverage.png

请注意,这些代码行的覆盖率缺失与需求 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");

这些函数会测试上面的三个错误代码。将代码复制并粘贴到 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

现在,测试涵盖了错误代码语句。

improvedCoverage.png

然而,在第 97 行有一个语句测试没有涵盖到。要求测试覆盖第 97 行的语句的条件还会导致执行第 87 行的 return,这意味着第 97 行的语句不可达,是死逻辑。

deadLogic.png

修复需求可追溯性缺失

重新生成可追溯性矩阵,应用与之前相同的过滤器,然后点击工具条中的突出显示缺失链接

slreq.generateTraceabilityMatrix(mtxOpts)
  • 顶部 > 链接 > 缺失链接

  • 顶部 > 类型 > 功能性

  • 左侧 > 类型 > 函数

  • 左侧 > 属性 > 测试

在错误代码需求与新测试之间创建链接。

createLinksImproveCoverage.png

通过重新运行链接到两个需求集的测试,在需求编辑器中更新验证状态。

status4 = runTests(funcReqs);
Running graph_unit_tests
.......... ..
Done graph_unit_tests
__________
status5 = runTests(testReqs);
Running graph_unit_tests
.......... ...
Done graph_unit_tests
__________

所有需求都有指向测试的链接,所有测试都顺利通过。

updatedVerificationStatus.png

跟踪生成的代码的需求

使用 Embedded Coder® 基于 shortest_path 算法生成代码,并包含使您能够跟踪生成代码的需求的需求注释。有关详细信息,请参阅从 MATLAB 代码生成的代码的需求可追溯性

创建代码配置对象以生成具有 LIB 编译类型的代码。

cfg = coder.config("lib","ecoder",true);

启用代码配置参数以在生成的代码中包含需求注释。

cfg.ReqsInCode = true;

使用 coder.typeof (MATLAB Coder) 定义最大大小为 100x100 的可变大小双精度数组和用作生成代码中的输入的双精度标量。

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

codegen-report-requirements-comments.png

shortest_path.c 文件包含带有链接需求摘要的注释、shortest_path.m 文件的完整文件路径以及链接的代码行。

另请参阅

| (MATLAB Coder) | (MATLAB Coder)

相关主题