Create Baseline Tests for MATLAB Code - MATLAB & Simulink (original) (raw)
In some cases, qualifying the output of MATLAB® code through automated testing is not straightforward. For example, a MATLAB function that performs complicated mathematical operations, such as solving a system of differential equations, might produce an output that requires manual inspection. With MATLAB Test™, you can create and run baseline tests to verify that such a function continues to produce the manually inspected "known good" output, referred to as_baseline data_. Baseline tests are useful for regression testing when you make changes to your code but require its output not to deviate from the baseline data.
This example shows how to create and run a baseline test for a simulator, which is implemented as a MATLAB function named pendulumSimulator
. First, you manually verify that the function works as expected. Then, you write a baseline test to create baseline data and verify consistent function behavior. ThependulumSimulator
function solves a system of differential equations to calculate the angular position and velocity of a pendulum at different points in time. To view the complete code for pendulumSimulator
, see Summary of Simulator Function.
Inspect Simulation Results
In a file named pendulumSimulator.m
in your current folder, create the pendulumSimulator
function. To manually verify that the function works as expected, call the function with a set of simulation parameters, and then plot the returned angular position and velocity values against the simulated time.
simParams.length = 1.5; simParams.stopTime = 10; simParams.initialAngle = pi/6; simParams.initialAngularVelocity = 0;
[t,y] = pendulumSimulator(simParams);
tiledlayout("horizontal") nexttile plot(t,y(:,1)) xlabel("Time (s)") ylabel("Angle (rad)") nexttile plot(t,y(:,2)) xlabel("Time (s)") ylabel("Angular Velocity (rad/s)")
The visualized data suggests that the simulated pendulum oscillates as expected. For example, the initial angular position and velocity are equal to the initial conditions specified in the simulation, and the oscillation period matches the theoretical value of T = 2πL/g with L and g, respectively, representing the pendulum length and gravitational acceleration. For illustrative purposes, this level of inspection is sufficient in this example. You can consider the inspected values as the "known good" output of the function under test.
Write Baseline Test for Simulator Function
You can write baseline tests for your MATLAB code by creating a test class using baseline-specific parameterization. Specify parameterization properties in a properties
block with theTestParameter
, MethodSetupParameter
, orClassSetupParameter
attribute. For more information about test parameterization, see Use Parameters in Class-Based Tests.
To write a baseline test for the pendulumSimulator
function, in a file named SimulatorTest.m
in your current folder, create theSimulatorTest
test class by subclassing the matlab.unittest.TestCase class. Define the baseline data to use in the test by adding two parameterization properties that correspond to the two output arguments of the pendulumSimulator
function. Then, use these properties to pass baseline data to a parameterizedTest
method, which implements the logic required for baseline testing. To view the complete code for SimulatorTest
, see Test Class Definition.
Define Baseline Data
To define the baseline data to use in the test, in a properties
block with the TestParameter
attribute, add two properties namedtime
and state
that correspond to the first and second output arguments of the pendulumSimulator
function. Set each property using the matlabtest.parameters.matfileBaseline function, which lets you define data in a MAT file as baseline data:
time
property — Define the data in variablet
of a MAT file namedtestdata.mat
as the baseline data for the first output argument ofpendulumSimulator
.state
property — Define the data in variabley
of a MAT file namedtestdata.mat
as the baseline data for the second output argument ofpendulumSimulator
.properties (TestParameter) time = matlabtest.parameters.matfileBaseline( ... "testdata.mat",VariableName="t") state = matlabtest.parameters.matfileBaseline( ... "testdata.mat",VariableName="y") end
The MAT file does not need to exist when you create a test class. If you use baseline-specific qualification methods, such as verifyEqualsBaseline, or the matlabtest.constraints.EqualsBaseline constraint, the testing framework provides you with options to either create the MAT file or update it upon a qualification failure.
Implement Test Logic
To implement the logic required for baseline testing, in amethods
block with the Test
attribute, add a parameterized Test
method named baselineTest
that accepts the time
and state
properties as inputs. Then, implement the method by following these steps:
Return the function outputs to test by calling
pendulumSimulator
using the same simulation parameters as in the manual verification of the function.Using a call to the
verifyEqualsBaseline
method, test the first function outputactualTime
against the baseline data represented by thetime
property.Using another call to the
verifyEqualsBaseline
method, test the second function outputactualState
against the baseline data represented by thestate
property.methods (Test) function baselineTest(testCase,time,state) simParams.length = 1.5; simParams.stopTime = 10; simParams.initialAngle = pi/6; simParams.initialAngularVelocity = 0; [actualTime,actualState] = pendulumSimulator(simParams); testCase.verifyEqualsBaseline(actualTime,time) testCase.verifyEqualsBaseline(actualState,state) end end
Test Class Definition
This code provides the complete contents of the SimulatorTest
class.
classdef SimulatorTest < matlab.unittest.TestCase properties (TestParameter) time = matlabtest.parameters.matfileBaseline( ... "testdata.mat",VariableName="t") state = matlabtest.parameters.matfileBaseline( ... "testdata.mat",VariableName="y") end
methods (Test)
function baselineTest(testCase,time,state)
simParams.length = 1.5;
simParams.stopTime = 10;
simParams.initialAngle = pi/6;
simParams.initialAngularVelocity = 0;
[actualTime,actualState] = pendulumSimulator(simParams);
testCase.verifyEqualsBaseline(actualTime,time)
testCase.verifyEqualsBaseline(actualState,state)
end
end
end
Run Test to Create Baseline Data
Now that the SimulatorTest
class definition is complete, you can run the baseline test to create baseline data in a MAT file and then verify consistent function behavior. In this example, you run the baseline test using the runtests function. You can also run the test interactively, for instance, using the MATLAB Test Manager app. For more information about ways to run tests, see Run MATLAB Tests.
Run the SimulatorTest
test class. The baseline test fails because thetestdata.mat
file does not yet exist and there is no baseline data to compare the actual values against. However, the test diagnostics include a hyperlink for each qualification failure that enables you to create baseline data from the actual value that the framework records.
result = runtests("SimulatorTest");
Running SimulatorTest
================================================================================ Verification failed in SimulatorTest/baselineTest(time=testdata.mat(t),state=testdata.mat(y)). --------------------- Framework Diagnostic: --------------------- verifyEqualsBaseline failed. --> An error occurred when loading baseline data: MATLAB:MatFile:NoFile Cannot access 't' because 'C:\work\testdata.mat' does not exist. --> Create baseline from recorded test data ------------------ Stack Information: ------------------ ... In C:\work\SimulatorTest.m (SimulatorTest.baselineTest) at 18
================================================================================ Verification failed in SimulatorTest/baselineTest(time=testdata.mat(t),state=testdata.mat(y)). --------------------- Framework Diagnostic: --------------------- verifyEqualsBaseline failed. --> An error occurred when loading baseline data: MATLAB:MatFile:NoFile Cannot access 'y' because 'C:\work\testdata.mat' does not exist. --> Create baseline from recorded test data ------------------ Stack Information: ------------------ ... In C:\work\SimulatorTest.m (SimulatorTest.baselineTest) at 19
. Done SimulatorTest
Failure Summary:
Name Failed Incomplete Reason(s)
=====================================================================================================================
SimulatorTest/baselineTest(time=testdata.mat(t),state=testdata.mat(y)) X Failed by verification.
Because you already manually verified the actual values before creating the baseline test, approve them as baseline data by clicking both the Create baseline from recorded test data hyperlinks. Each time you click Create baseline from recorded test data, the testing framework saves the corresponding actual value to the MAT file and then displays a dialog box to confirm the operation. For example, when you click the first Create baseline from recorded test data hyperlink, the framework creates thetestdata.mat
file and saves the recorded values inactualTime
as variable t
to the file.
Now that baseline data exists for your test, you can verify that thependulumSimulator
function continues to produce the same outputs by running the baseline test. If a function output changes, for instance, due to code refactoring, the baseline test signals the change through a qualification failure. The test passes only if the function outputs remain the same as the baseline data in the MAT file. For instance, run the test again to test the function outputs against the baseline data in the t
and y
variables oftestdata.mat
. The test passes becausependulumSimulator
has not changed since baseline data creation.
result = runtests("SimulatorTest");
Running SimulatorTest . Done SimulatorTest
Run Test After Updating Function
Whenever you make changes to your MATLAB code, run the baseline tests to confirm that the code behavior aligns with the established baseline. If a baseline test fails, investigate the failure using the test diagnostics or debug it using the load method to identify the root cause. Baseline tests can be fragile due to their strict equality checks, meaning that a failure might result from the inherent fragility of the test itself. If you determine that the code behaves as expected, update the existing baseline data using the hyperlinks in the test diagnostics.
The pendulumSimulator
function uses the ode45 solver to simulate the pendulum motion. For higher accuracy, update pendulumSimulator
to use the ode78 solver instead.
function [t,y] = pendulumSimulator(params) % Simulation parameters l = params.length; tspan = [0 params.stopTime]; y0 = [params.initialAngle params.initialAngularVelocity];
odefun = @(t,y) pendulum(t,y,l); [t,y] = ode78(odefun,tspan,y0); % Solve the pendulum equations end
% System of differential equations to solve function dydt = pendulum(~,y,l) g = 9.8; % Gravitational acceleration dydt(1,1) = y(2); dydt(2,1) = -(g/l) * sin(y(1)); end
Because you updated the pendulumSimulator
function, run the baseline test in the SimulatorTest
class to compare the new function outputs against the baseline data in testdata.mat
. The test fails because the ode78
solver results in values that are not strictly equal to the existing baseline data. In particular, the test diagnostics indicate that the actual and baseline array sizes are not the same.
result = runtests("SimulatorTest");
Running SimulatorTest
================================================================================
Verification failed in SimulatorTest/baselineTest(time=testdata.mat(t),state=testdata.mat(y)).
---------------------
Framework Diagnostic:
---------------------
verifyEqualsBaseline failed.
--> NumericComparator failed.
--> Sizes do not match.
Actual size:
145 1
Expected size:
149 1
Actual Value:
145x1 double
Expected Value:
149x1 double
--> Export test data to workspace | Update baseline from recorded test data
------------------
Stack Information:
------------------
...
In C:\work\SimulatorTest.m (SimulatorTest.baselineTest) at 18
================================================================================
Verification failed in SimulatorTest/baselineTest(time=testdata.mat(t),state=testdata.mat(y)).
---------------------
Framework Diagnostic:
---------------------
verifyEqualsBaseline failed.
--> NumericComparator failed.
--> Sizes do not match.
Actual size:
145 2
Expected size:
149 2
Actual Value:
145x2 double
Expected Value:
149x2 double
--> Export test data to workspace | Update baseline from recorded test data
------------------
Stack Information:
------------------
...
In C:\work\SimulatorTest.m (SimulatorTest.baselineTest) at 19
. Done SimulatorTest
Failure Summary:
Name Failed Incomplete Reason(s)
=====================================================================================================================
SimulatorTest/baselineTest(time=testdata.mat(t),state=testdata.mat(y)) X Failed by verification.
To further investigate each qualification failure, export the actual value and the corresponding baseline data to the workspace by clicking Export test data to workspace in the test diagnostics. The framework exports the values asactual
and baseline
and displays a dialog box to confirm the operation.
For example, visualize the actual evaluation points (variableactualTime
in the test file) and baseline evaluation points (variable t
in the MAT file) using the values that are exported when you click the first Export test data to workspace hyperlink. Despite slight differences, both the actual
andbaseline
vectors cover the entire simulation time.
plot(actual) hold on plot(baseline) legend("Actual","Baseline",Location="northwest")
After verifying the new function implementation using the exported test data, update the existing baseline data in the MAT file. To update the baseline data for a specific qualification, click the Update baseline from recorded test data hyperlink in the test diagnostics. The testing framework displays a dialog box, prompting you to confirm the update operation. Once you choose to proceed, the framework updates the MAT file using the new actual value.
For this example, update the baseline data in the t
andy
variables of testdata.mat
by clicking both the Update baseline from recorded test data hyperlinks. Then, run the baseline test. The test passes because the updated baseline data in the MAT file matches the outputs that the new version of pendulumSimulator
produces.
result = runtests("SimulatorTest");
Running SimulatorTest . Done SimulatorTest
Summary of Simulator Function
This code provides the contents of the pendulumSimulator
function, which uses the ode45 solver to simulate the motion of a pendulum, assuming no friction or air resistance.
function [t,y] = pendulumSimulator(params) % Simulation parameters l = params.length; tspan = [0 params.stopTime]; y0 = [params.initialAngle params.initialAngularVelocity];
odefun = @(t,y) pendulum(t,y,l); [t,y] = ode45(odefun,tspan,y0); % Solve the pendulum equations end
% System of differential equations to solve function dydt = pendulum(~,y,l) g = 9.8; % Gravitational acceleration dydt(1,1) = y(2); dydt(2,1) = -(g/l) * sin(y(1)); end
The function accepts the simulation parameters, expressed in SI units, as a scalar structure with these fields:
length
— Length of the pendulumstopTime
— Duration of the simulationinitialAngle
— Initial angular position of the pendulum measured from the equilibrium axisinitialAngularVelocity
— Initial angular velocity of the pendulum
The function returns the evaluation points and solutions as numeric arrays:
t
— Evaluation points, returned as an N-by-1 column vectory
— Solutions, returned as an N-by-2 matrix in which the first and second columns, respectively, represent the angular position and velocity of the pendulum
See Also
Functions
Classes
- matlab.unittest.TestCase | matlabtest.parameters.BaselineParameter | matlabtest.baselines.MATFileBaseline | matlabtest.constraints.EqualsBaseline | matlabtest.selectors.HasBaseline