使用向导对导入的自定义代码进行单元测试
此示例显示如何使用代码导入器向导将热泵控制器的自定义 C 代码导入 Simulink 进行单元测试。单元测试独立于自定义代码库测试一个或多个函数。对于单元测试,Simulink Test 代码导入器向导会生成一个测试沙箱和一个库,其中包含来自自定义代码的每个指定函数的 C Caller 模块。有关更多信息,请参阅导入和测试自定义 C/C++ 代码。
此示例仅使用 tempController.c
文件创建并导入测试沙箱到 Simulink。您使用沙箱仅对 heatpumpController
文件中的 tempController.c
函数执行单元测试,而不是对完整代码执行单元测试。生成沙盒会自动为 heatpumpController
函数、absoluteTempDifference
和 pumpDirection
使用的实用函数创建桩件。由于实用函数未在 tempController.c
文件中定义,并且未包含 utils.c
和 utils.h
文件,因此代码导入器会创建桩件,因此代码不会出错。
热泵控制器自定义代码
热泵控制器的完整代码位于以下 C 代码源和头文件中:
源文件位于 src
目录中:
tempController.c
utils.c
头文件位于 include
目录中:
tempController.h
utils.h
controllerTypes.h
tempController.c
文件包含热泵机组的自定义 C 代码的算法。该文件中的 heatpumpController
函数使用室温 (Troom_in
) 和设定温度 (Tset
) 作为输入。输出是 pump_control_bus
类型结构体,其信号控制风扇、热泵以及热泵的方向(加热或冷却)。pump_control_bus
结构体中的字段是 fan_cmd
、pump_cmd
和 pump_dir
。pump_control_bus
结构体类型在 controllerTypes.h
文件中定义。heatpumpController
函数的输出是:
heatpumpController
算法的输出总结如下表:
heatpumpController
函数使用了两个实用函数 absoluteTempDifference
和 pumpDirection
,它们在 utils.c
文件中定义。absoluteTempDifference
函数以双精度形式返回 Tset
和 Troom_in
之间的绝对差。pumpDirection
函数返回以下 PumpDirection
类型枚举值之一:
PumpDirection
枚举 type
在 controllerTypes.h
文件中定义。
打开代码导入向导
要打开 Simulink Test C/C++ 代码导入器向导,首先打开 Simulink Test 管理器。
sltest.testmanager.view
然后,选择新建 > 测试 C/C++ 代码。
指定 Simulink 库和测试方法
向导打开并显示欢迎选项卡。点击开始即可开始导入过程。
在设置选项卡上:
输入 Simulink 库文件名。生成的 Simulink 库、测试沙盒目录和测试文件(MLDATX)使用此名称。输入
heatpumpController
。指定用于保存所生成工件的输出文件夹。输入可写文件夹的名称。
选择 C 代码单元测试作为测试方法。
C 代码单元测试方法单独测试一个 C 文件或自定义代码的子集。此方法从指定的源文件或文件创建测试沙盒,并将测试沙盒导入 Simulink。由于测试沙箱仅包含 C 文件的子集,因此代码导入器向导会自动为 C 文件中使用的所有未定义符号创建桩件。此方法仅支持测试 C 代码。有关集成测试的信息,请参阅 sltest.CodeImporter
的 TestType
属性。
点击下一步。
指定要导入的自定义代码
在指定自定义代码选项卡上:
在源文件中,指定包含要导入进行单元测试的函数的源文件。输入
.\src\tempController.c
。在包含目录中,指定指定的源文件所依赖的目录。输入
.\include
。Defines 指定特定于编译器的定义。对于此示例,将此字段留空。
点击下一步。
指定测试沙盒设置
在分析选项卡上,指定沙盒设置。
选择输出测试沙盒模式
三种测试沙盒模式中的每一种都有设置来决定代码导入器向导如何生成沙盒和沙盒工件。
选择生成聚合标头。该测试沙盒模式为指定的源文件生成最小的聚合头文件。聚合头文件包含指定源文件所使用的符号的所有声明。
选择输出测试沙盒设置
仅选择复制源文件。对于此选项,代码导入器向导将指定的源文件复制到沙箱 src
目录。
有关其他输出测试沙盒模式和设置的信息,请参阅 sltest.CodeImporter.SandboxSettings
的属性。
点击下一步以使用指定的设置创建测试沙盒。
创建测试沙盒
指定的库文件名决定了生成的沙箱的名称。对于此示例,沙盒名称是 heatpumpController_sandbox
。
此示例使用 tempController.c
并仅对 heatpumpController
函数执行单元测试。生成沙盒会自动为 absoluteTempDifference
函数使用的 pumpDirection
和 heatpumpController
实用函数创建桩件,由于实用函数未在 tempController.c
文件中定义,因此桩件会阻止代码出错。
创建沙箱后,将显示确认屏幕。
输出沙盒文件夹包含以下子文件夹:
src:此文件夹包含复制的源文件,本例中为
tempController.c
。include
:根据测试沙盒模式,该文件夹包含aggregatedHeader.h
或interfaceHeader.h
文件。此示例使用aggregatedHeader.h
文件。该文件夹还包含编译指定的源文件所需的其他头文件的副本。aggregatedHeader.h
文件包含指定源文件所使用的所有符号的声明。interfaceHeader.h
包含指定源文件使用的函数、全局变量和类型的声明。Simulink 在导入过程中使用生成的接口头文件。autostub
:此文件夹包含auto_stub.c
和auto_stub.h
文件,其中保存了absoluteTempDifference
和pumpDirection
实用函数.
的自动生成的桩件manualstub
:此文件夹包含man_stub.c
和man_stub.h
文件,它们定义任何手动指定的桩件。默认情况下,这些文件没有定义任何函数。
注意:请勿修改生成的沙盒的 src
、include
或 autosub
文件夹中的文件。
沙盒创建完成后,可以查看创建的文件:
点击 Auto stub source 和 Auto stub header 分别打开
auto_stub.c
和 auto_stub.h 文件。在auto_stub.c
中,您可以找到heatpumpController
、absoluteTempDifference
和pumpDirection
使用的实用函数的桩件。在auto_stub.h
中,您可以找到桩件函数的extern
声明。点击手动桩件标头和手动桩件源分别打开
man_stub.h
和man_stub.c
文件。由于您为沙盒模式选择了生成聚合标头,因此沙盒中man_stub.h
包含了aggregatedHeader.h
文件。默认情况下,手动桩件文件没有任何函数定义。
点击下一步。
指定导入设置
指定使用全局变量作为函数接口
当向导检测到沙箱或自定义代码中的全局变量时,您可以选择使用这些变量作为创建的 C Caller 模块上的函数和端口的输入或输出。有关更多信息,请参阅Automatically infer global variables as function interfaces。
选择启用全局变量作为函数接口选项。
点击下一步。
选择要导入的函数
在导入页面,选择 heatpumpController
函数导入到 Simulink 库中。
点击下一步。
设置模块端口规格
对于您在上一页中选择的功能,向导将生成功能端口规范。选定的端口用于生成的 C Caller 模块。请注意,如果要导入的代码具有带指针输出的函数,则需要在向导的此页面上指定输出端口的大小。
在此示例中,端口规范表列出了 Tset
函数的正式参量 Troom_in
、out
和 heatpumpController
,以及来自自动生成的桩件的另外六个参数。由于您选择了启用全局变量作为函数接口,因此向导会创建从 absoluteTempDifference
和 pumpDirection
函数的桩件生成的端口。桩件在 auto_stub.c
中。
这些是 auto_stub.c
文件中自动生成的全局变量:
/*************************************************************************/ /* Generated Global Variables for Stubbed Functions Interface */ /*************************************************************************/ double SLStubIn_absoluteTempDifference_p1; double SLStubIn_absoluteTempDifference_p2; double SLStubOut_absoluteTempDifference; double SLStubIn_pumpDirection_p1; double SLStubIn_pumpDirection_p2; PumpDirection SLStubOut_pumpDirection; double absoluteTempDifference(double absoluteTempDifference_p1, double absoluteTempDifference_p2) { SLStubIn_absoluteTempDifference_p1 = absoluteTempDifference_p1; SLStubIn_absoluteTempDifference_p2 = absoluteTempDifference_p2; return SLStubOut_absoluteTempDifference; } PumpDirection pumpDirection(double pumpDirection_p1, double pumpDirection_p2) { SLStubIn_pumpDirection_p1 = pumpDirection_p1; SLStubIn_pumpDirection_p2 = pumpDirection_p2; return SLStubOut_pumpDirection; }
在自动生成的 absoluteTempDifference
的桩件中,全局变量 SLStubIn_absoluteTempDifference_p1
和 SLStubIn_absoluteTempDifference_p2
保存了输入参量。该函数返回存储在 SLStubOut_absoluteTempDifference
中的值作为其输出。类似地,pumpDirection
保存输入参量并返回 SLStubOut_pumpDirection
。
在此示例中,absoluteTempDifference
、SLStubIn_absoluteTempDifference_p1
和 SLStubIn_absoluteTempDifference_p2
是输出。全局变量 SLStubOut_absoluteTempDifference
是一个输入。
不要对端口规范做任何更改。点击下一步。
指定要导入的类型
选择要导入到 Simulink 的类型。因为 pump_control_bus
和 PumpDirection
类型是 heatpumpController 函数所需要的,所以它们被选中并变暗。向导创建一个包含这些类型的 Simulink 数据字典,并将该字典链接到生成的库。
点击下一步显示生成的库的摘要。再次点击下一步继续。
创建测试框架
选择自动为所有导入的功能创建测试框架。
点击下一步生成 Simulink 库。
代码导入后,向导会创建一个附加到 Simulink 数据字典的库,该库分别将 pump_control_bus
和 PumpDirection
定义为 Simulink.Bus
对象和 Simulink 枚举信号。
在 Simulink 库中创建的 C Caller 模块是:
点击模块的右下角以打开其内部测试框架:
在代码导入成功页面上,点击将 MATLAB 文件夹更改为输出文件夹。
请不要点击完成。继续下一部分,手动更新未定义符号的桩件。
使用手动插桩更新测试沙箱
要手动创建 absoluteTempDifference
和 pumpDirection
的桩件文件,点击向导工具栏中的分析选项卡,然后点击下一步两次以显示此页面:
代码导入器检测您创建的沙箱。选择覆盖,然后点击下一步。
在向导之外,但保持向导打开,复制 updated_manualstub
目录中的文件并将其粘贴到沙箱 manualstub
的 .
目录中
在 man_stub.c
文件中,编辑 absoluteTempDifference
的函数定义:
double absoluteTempDifference(double Tset, double Troom_in){ return (double)fabs(Tset - Troom_in); }
以及 pumpDirection
的函数定义:
PumpDirection pumpDirection(double Tset, double Troom_in){ return Tset > Troom_in ? HEATING : COOLING; }
返回代码导入器向导。
修改手动桩件文件后,返回向导并点击更新沙盒。
因为您手动定义了 absoluteTempDifference
和 pumpDirection
函数的定义,所以没有符号可以自动桩件,沙箱中的 autostub
目录没有生成工件,并且它是空的。
点击下一步,直到到达模块端口规范页面:
因为 absoluteTempDifference
和 pumpDirection
的手动实现没有任何全局变量,所以只有形式参量和返回参量作为端口出现。
点击下一步。
在创建 Simulink 库选项卡上,选择覆盖。
点击下一步,浏览剩余页面,直到到达代码导入成功页面。
然后点击完成。如果需要,请在打开的对话框中点击是,将当前导入设置保存为 JSON 文件,然后使用该文件将设置重新加载到另一个代码导入器向导会话中。
测试导入的代码
在测试管理器的测试浏览器窗格中展开 heatpumpController 测试文件和 heatpumpController/heatpumpController 测试套件。然后选择 heatpumpController_Harness1 测试用例。
指定仿真停止时间
在在测系统部分中,展开仿真设置和发布覆盖部分,并将停止时间设置为 200。
添加输入
在输入部分中,在外部输入表的底部,点击添加以打开添加输入对话框。
在输入文件规范中,输入 heatpumpControllerHarnessInput.xlsx
。
在输入映射下,选择 Block Name
作为映射模式,然后点击映射输入。输入出现在映射状态表中后,点击确定。
添加评估
在逻辑和时序评估部分,添加针对每种温度条件的评估:
有关评估的信息,请参阅 使用时序评估来评估时序逻辑。
在符号窗格中,添加符号定义:
运行测试并查看结果
点击运行以运行测试。
测试完成后,在结果和工件窗格中,展开结果。全部评估均通过。
要查看覆盖率结果,请选择结果下的 heatpumpController_Harness1,并展开覆盖率结果部分。tempController.c
和 man_stub.c
文件的覆盖率均为 100%。