Write Independent and Repeatable Tests
Unit tests must run independently, without unintentionally affecting one another. In
addition, unit tests must be repeatable, which means that a running test must not affect
subsequent reruns of the same test. When you create class-based tests by subclassing the
matlab.unittest.TestCase
class, use
the following best practices for test independence and repeatability.
Specify Symmetric Setup and Teardown Actions
If your test class includes code for setting up the test environment, it must also include code to restore the environment to its original state by performing teardown actions symmetrically in the reverse order of their corresponding setup actions:
For setup code specified in a
TestClassSetup
methods
block, specify the corresponding teardown code by using theaddTeardown
method in the same block or by specifying aTestClassTeardown
methods
block.For setup code specified in a
TestMethodSetup
methods
block, specify the corresponding teardown code by using theaddTeardown
method in the same block or by specifying aTestMethodTeardown
methods
block.For setup code specified using a shared test fixture, do not add separate teardown code to your test class. The fixture automatically restores the environment when the testing framework tears it down. Shared test fixtures are specified using the
SharedTestFixtures
attribute ofTestCase
subclasses.
For example, in the AsymmetryExampleTest
class, the
setFormat
method runs a single time before the tests because it
is defined in a TestClassSetup
methods
block. However, the restoreFormat
method
runs after each test in the test class because it is defined in a
TestMethodTeardown
methods
block. If you set the line spacing format to its default
value (format loose
) and then run the
AsymmetryExampleTest
class, one of the tests in the test class
fails because the restoreFormat
method restores the format to its
original state after running the first test. In other words, the teardown code runs
earlier than expected. In contrast, the tests in the
SymmetryExampleTest
class pass because the
addTeardown
method call runs the teardown code only after both
the tests in the class run. Therefore, the format remains the same during
testing.
Not Recommended | Recommended |
---|---|
classdef AsymmetryExampleTest < matlab.unittest.TestCase properties OriginalFormat end methods (TestClassSetup) function setFormat(testCase) testCase.OriginalFormat = format; format("compact") end end methods (TestMethodTeardown) function restoreFormat(testCase) format(testCase.OriginalFormat) end end methods (Test) function formatTest1(testCase) testCase.verifyEqual(format().LineSpacing,"compact") end function formatTest2(testCase) testCase.verifyEqual(format().LineSpacing,"compact") end end end |
classdef SymmetryExampleTest < matlab.unittest.TestCase methods (TestClassSetup) function setFormat(testCase) originalFormat = format; testCase.addTeardown(@format,originalFormat) format("compact") end end methods (Test) function formatTest1(testCase) testCase.verifyEqual(format().LineSpacing,"compact") end function formatTest2(testCase) testCase.verifyEqual(format().LineSpacing,"compact") end end end |
It is recommended that you perform all teardown actions from
within the TestMethodSetup
and TestClassSetup
methods
blocks using the addTeardown
method instead of
implementing corresponding teardown methods in the TestMethodTeardown
and
TestClassTeardown
methods
blocks. Call addTeardown
immediately before or after
the original state change, without any other code in between that can throw an exception. Using
addTeardown
allows the testing framework to execute the teardown code in
the reverse order of the setup code and also creates exception-safe test content.
For more information about setup and teardown code in test classes, see Write Setup and Teardown Code Using Classes and Write Tests Using Shared Fixtures.
Access Global State Using Methods
To access a global state, such as the MATLAB® search path or output display format, use a method instead of a property. Specifying the state as a default property value is not recommended because accessing the property value at different points might not reflect changes to the state. MATLAB evaluates default property values a single time, when parsing the class.
For example, in the PropertyExampleTest
class, the
OriginalFormat
property is set to the display format at
class parse time, and its value remains the same in different runs of the test
class. If you change the line spacing format after the first run, the property still
points to the original line spacing format from class parse time. As a result, a
second run of the test class corrupts the display format because the
addTeardown
method call always restores the format to its value
at class parse time and not the value prior to the test run. In contrast, in the
MethodExampleTest
class, the current display format is correctly
captured because the formatTest
method runs every time the test
class runs. Therefore, the addTeardown
method call in the
MethodExampleTest
class can restore the display format to its
value prior to the test run, and the test does not corrupt the display
format.
Not Recommended | Recommended |
---|---|
classdef PropertyExampleTest < matlab.unittest.TestCase properties OriginalFormat = format end methods (Test) function formatTest(testCase) testCase.addTeardown(@format,testCase.OriginalFormat) format("compact") % Test code end end end |
classdef MethodExampleTest < matlab.unittest.TestCase methods (Test) function formatTest(testCase) originalFormat = format; testCase.addTeardown(@format,originalFormat) format("compact") % Test code end end end |
Use Value Objects as Test Parameter Values
Initialize your parameterization properties with value objects. Using handle objects, such as MATLAB graphics objects, as parameter values is not recommended. If you need to test handle objects in your parameterized test, consider constructing them indirectly by using function handles as parameter values and invoking those function handles in tests.
For example, the first time you run the HandleExampleTest
class,
the parameterized tests successfully run using the figures created in the
properties
block. After testing, each test closes the figure
passed to it as a parameter value using a call to the addTeardown
method. If you run the test class again, the tests fail because MATLAB evaluates default property values a single time, when parsing the
class. In other words, there are no figures to test with in subsequent runs of the
HandleExampleTest
class. In contrast, the tests in the
ValueExampleTest
class are repeatable because each parameterized
test creates its own figure by invoking a function handle and then closes the figure
after testing.
Not Recommended | Recommended |
---|---|
classdef HandleExampleTest < matlab.unittest.TestCase properties (TestParameter) fig = {figure,uifigure} end methods (Test) function figureTest(testCase,fig) testCase.addTeardown(@close,fig) cp = fig.CurrentPoint; testCase.verifyEqual(cp,[0 0]) end end end |
classdef ValueExampleTest < matlab.unittest.TestCase properties (TestParameter) fig = {@figure,@uifigure} end methods (Test) function figureTest(testCase,fig) f = fig(); testCase.addTeardown(@close,f) cp = f.CurrentPoint; testCase.verifyEqual(cp,[0 0]) end end end |
For more information about test parameters, see Use Parameters in Class-Based Tests.