Write Independent and Repeatable Tests - MATLAB & Simulink (original) (raw)

Main Content

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 thematlab.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 example, in the AsymmetryExampleTest class, thesetFormat 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 aTestMethodTeardown methods block. If you set the line spacing format to its default value (format loose) and then run theAsymmetryExampleTest 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 theSymmetryExampleTest class pass because theaddTeardown 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 andTestClassTeardown methods blocks. Call addTeardown immediately before or after the original state change, without any other code in between that can throw an exception. UsingaddTeardown 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, theOriginalFormat 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 theaddTeardown 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 theMethodExampleTest 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 theMethodExampleTest 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 theproperties 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 theHandleExampleTest class. In contrast, the tests in theValueExampleTest 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.

See Also

Classes

Topics