编写插件以将数据添加到测试结果
此示例说明如何创建一个插件,它将数据添加到 TestResult
对象。该插件将断言中的实际值和预期值追加到 TestResult
对象的 Details
属性。为了扩展 TestRunner
,该插件会覆盖 matlab.unittest.plugins.TestRunnerPlugin
类的所选方法。
创建插件类
在当前文件夹中的文件中,创建自定义插件类 DetailsRecordingPlugin
,该类继承自 TestRunnerPlugin
类。有关 DetailsRecordingPlugin
的完整代码,请参阅 DetailsRecordingPlugin 类定义总结。
要在 TestResult
对象中存储实际值和预期值,请在 properties
代码块中定义两个常量属性 ActField
和 ExpField
。将 ActField
的值设置为包含实际值的 Details
结构体的字段的名称。将 ExpField
的值设置为包含预期值的字段的名称。
properties (Constant, Access = private) ActField = 'ActualValue'; ExpField = 'ExpectedValue'; end
将字段添加到详细信息属性
要向属于测试会话的所有 TestResult
对象的 Details
属性添加新字段,请在访问权限为 protected
的 methods
代码块中覆盖 TestRunnerPlugin
的 runSession
方法。runSession
向 TestResult
对象的 Details
结构体添加两个空字段,并调用超类方法来触发整个测试运行。
methods (Access = protected) function runSession(plugin,pluginData) resultDetails = pluginData.ResultDetails; resultDetails.append(plugin.ActField,{}) resultDetails.append(plugin.ExpField,{}) runSession@matlab.unittest.plugins.TestRunnerPlugin(plugin,pluginData); end end
为了添加字段,runSession
的实现包含对 matlab.unittest.plugins.plugindata.ResultDetails
类的 append
方法的调用。每次调用都会向 Details
结构体中添加一个空字段。
扩展共享测试脚手架和 TestCase 实例的创建
通过扩展测试框架用于创建测试内容的方法,为 AssertionPassed
和 AssertionFailed
事件添加侦听程序。测试内容包括每个 Test
元素的 TestCase
实例,TestClassSetup
和 TestClassTeardown
方法代码块的类级别 TestCase
实例,以及当 TestCase
类具有 SharedTestFixtures
属性时使用的 Fixture
实例。
在覆盖创建方法时调用相应的超类方法。每当执行断言时,您添加到返回的 Fixture
或 TestCase
实例的侦听程序都会导致 reactToAssertion
辅助方法执行。要将断言数据添加到测试结果,请将结果修改器实例与断言事件侦听程序数据一起传递给辅助方法。
将这些创建方法添加到具有 protected
访问权限的 methods
代码块。
methods (Access = protected) function fixture = createSharedTestFixture(plugin, pluginData) fixture = createSharedTestFixture@... matlab.unittest.plugins.TestRunnerPlugin(plugin, pluginData); resultDetails = pluginData.ResultDetails; fixture.addlistener('AssertionPassed',... @(~,evd)plugin.reactToAssertion(evd,resultDetails)); fixture.addlistener('AssertionFailed',... @(~,evd)plugin.reactToAssertion(evd,resultDetails)); end function testCase = createTestClassInstance(plugin,pluginData) testCase = createTestClassInstance@... matlab.unittest.plugins.TestRunnerPlugin(plugin,pluginData); resultDetails = pluginData.ResultDetails; testCase.addlistener('AssertionPassed',... @(~,evd)plugin.reactToAssertion(evd,resultDetails)); testCase.addlistener('AssertionFailed',... @(~,evd)plugin.reactToAssertion(evd,resultDetails)); end function testCase = createTestMethodInstance(plugin,pluginData) testCase = createTestMethodInstance@... matlab.unittest.plugins.TestRunnerPlugin(plugin,pluginData); resultDetails = pluginData.ResultDetails; testCase.addlistener('AssertionPassed',... @(~,evd)plugin.reactToAssertion(evd,resultDetails)); testCase.addlistener('AssertionFailed',... @(~,evd)plugin.reactToAssertion(evd,resultDetails)); end end
定义辅助方法
在访问权限为 private
的 methods
代码块中,定义辅助方法 reactToAssertion
。此方法使用 QualificationEventData
实例基于 IsEqualTo
约束提取断言中的实际值和预期值,将提取的值转换为元胞数组,并将元胞数组追加到对应的 TestResult
对象的字段中。
methods (Access = private) function reactToAssertion(plugin,evd,resultDetails) if ~isa(evd.Constraint,'matlab.unittest.constraints.IsEqualTo') return end resultDetails.append(plugin.ActField,{evd.ActualValue}) resultDetails.append(plugin.ExpField,{evd.Constraint.Expected}) end end
DetailsRecordingPlugin 类定义总结
以下代码提供 DetailsRecordingPlugin
的完整内容。
classdef DetailsRecordingPlugin < matlab.unittest.plugins.TestRunnerPlugin properties (Constant, Access = private) ActField = 'ActualValue'; ExpField = 'ExpectedValue'; end methods (Access = protected) function runSession(plugin,pluginData) resultDetails = pluginData.ResultDetails; resultDetails.append(plugin.ActField,{}) resultDetails.append(plugin.ExpField,{}) runSession@matlab.unittest.plugins.TestRunnerPlugin(plugin,pluginData); end function fixture = createSharedTestFixture(plugin, pluginData) fixture = createSharedTestFixture@... matlab.unittest.plugins.TestRunnerPlugin(plugin, pluginData); resultDetails = pluginData.ResultDetails; fixture.addlistener('AssertionPassed',... @(~,evd)plugin.reactToAssertion(evd,resultDetails)); fixture.addlistener('AssertionFailed',... @(~,evd)plugin.reactToAssertion(evd,resultDetails)); end function testCase = createTestClassInstance(plugin,pluginData) testCase = createTestClassInstance@... matlab.unittest.plugins.TestRunnerPlugin(plugin,pluginData); resultDetails = pluginData.ResultDetails; testCase.addlistener('AssertionPassed',... @(~,evd)plugin.reactToAssertion(evd,resultDetails)); testCase.addlistener('AssertionFailed',... @(~,evd)plugin.reactToAssertion(evd,resultDetails)); end function testCase = createTestMethodInstance(plugin,pluginData) testCase = createTestMethodInstance@... matlab.unittest.plugins.TestRunnerPlugin(plugin,pluginData); resultDetails = pluginData.ResultDetails; testCase.addlistener('AssertionPassed',... @(~,evd)plugin.reactToAssertion(evd,resultDetails)); testCase.addlistener('AssertionFailed',... @(~,evd)plugin.reactToAssertion(evd,resultDetails)); end end methods (Access = private) function reactToAssertion(plugin,evd,resultDetails) if ~isa(evd.Constraint,'matlab.unittest.constraints.IsEqualTo') return end resultDetails.append(plugin.ActField,{evd.ActualValue}) resultDetails.append(plugin.ExpField,{evd.Constraint.Expected}) end end end
创建示例测试类
在您的当前文件夹中,创建一个名为 ExampleTest.m
的文件,其中包含以下参数化测试类。该类生成一个包含 25 个元素的测试套件,每个元素对应于使用不同随机数生成器种子进行的一次试验。在每次试验中,测试框架创建一个由正态分布随机数组成的 1×100 向量,并断言实际样本均值和预期样本均值之间的差的幅值在 0.1 以内。
classdef ExampleTest < matlab.unittest.TestCase properties SampleSize = 100; end properties (TestParameter) seed = num2cell(randi(10^6,1,25)); end methods(Test) function testMean(testCase,seed) import matlab.unittest.constraints.IsEqualTo import matlab.unittest.constraints.AbsoluteTolerance rng(seed) testCase.assertThat(mean(randn(1,testCase.SampleSize)),... IsEqualTo(0,'Within',AbsoluteTolerance(0.1))); end end end
将插件添加到 TestRunner 并运行测试
在命令提示符下,基于 ExampleTest
类创建测试套件。
import matlab.unittest.TestSuite import matlab.unittest.TestRunner suite = TestSuite.fromClass(?ExampleTest);
创建一个没有插件的 TestRunner
实例。此代码创建一个静默运行程序,以便您控制安装的插件。
runner = TestRunner.withNoPlugins;
将 DetailsRecordingPlugin
添加到运行程序并运行测试。
runner.addPlugin(DetailsRecordingPlugin) result = runner.run(suite)
result = 1×25 TestResult array with properties: Name Passed Failed Incomplete Duration Details Totals: 18 Passed, 7 Failed (rerun), 7 Incomplete. 0.12529 seconds testing time.
要检索有关随机数生成行为的详细信息,请根据测试结果的 Details
结构体创建一个结构体数组。
details = [result.Details]
details = 1×25 struct array with fields: ActualValue ExpectedValue
创建一个包含每个测试中实际值和预期值之间差的数组,然后在条形图中显示误差值。长度大于 0.1 的七个条形对应于失败的测试。
errorInMean = cell2mat([details.ExpectedValue]) - cell2mat([details.ActualValue]); bar(errorInMean) xlabel('Experiment') ylabel('Error')
另请参阅
matlab.unittest.plugins.TestRunnerPlugin
| matlab.unittest.TestRunner
| matlab.unittest.fixtures.Fixture
| matlab.unittest.TestSuite
| addlistener
| matlab.unittest.TestResult
| matlab.unittest.plugins.plugindata.ResultDetails