Main Content

Create Advanced Custom Fixture

This example shows how to create a custom fixture that changes the output display format for numeric values. You can apply the fixture to a single test class or share the fixture across multiple test classes. After testing, the fixture restores the display format to its original state.

Create NumericFormatFixture Class

In a file named NumericFormatFixture.m in your current folder, create the NumericFormatFixture class by deriving from the matlab.unittest.fixtures.Fixture interface. Because you want to pass the fixture a numeric format, add a Format property to your class.

    properties (SetAccess=immutable)
        Format (1,1) string
    end

Add Fixture Constructor

In a methods block in your class, define a constructor that sets the Format property.

    methods
        function fixture = NumericFormatFixture(fmt)
            fixture.Format = fmt;
        end
    end

Implement setup Method

Subclasses of the Fixture interface must implement the setup method, which makes changes to the environment when the testing framework sets up the fixture. To restore the environment when the framework tears down the fixture, you can call the addTeardown method within the setup method.

In a methods block, implement the setup method to change the numeric format to the format specified during fixture construction and to restore the format to its original state after testing. To provide descriptive information when the framework sets up and tear downs the fixture, set the SetupDescription and TeardownDescription properties within the method.

    methods
        function setup(fixture)
            originalFormat = format;
            fixture.addTeardown(@format,originalFormat)
            format(fixture.Format)
            fixture.SetupDescription = "Set the numeric format to " + ...
                fixture.Format + ".";
            fixture.TeardownDescription =  ...
                "Restored the numeric format to " + ...
                originalFormat.NumericFormat + ".";
        end
    end

Implement isCompatible Method

Implement the isCompatible method in your Fixture subclass if the fixture is configurable (for instance, if its class constructor accepts input arguments). In this example, because you set the Format property using the class constructor, you must implement isCompatible.

The testing framework calls isCompatible to determine whether instances of the same Fixture subclass correspond to the same shared test fixture state. The information about fixture compatibility helps the framework decide when to perform teardown and setup actions. Two NumericFormatFixture instances make the same change to the environment when their Format properties are equal. Specify this compatibility definition by implementing the isCompatible method in a methods block with protected access.

    methods (Access=protected)
        function tf = isCompatible(fixture1,fixture2)
            tf = fixture1.Format == fixture2.Format;
        end
    end

Fixture Class Definition

This code provides the complete contents of the NumericFormatFixture class.

classdef NumericFormatFixture < matlab.unittest.fixtures.Fixture
    properties (SetAccess=immutable)
        Format (1,1) string
    end

    methods
        function fixture = NumericFormatFixture(fmt)
            fixture.Format = fmt;
        end

        function setup(fixture)
            originalFormat = format;
            fixture.addTeardown(@format,originalFormat)
            format(fixture.Format)
            fixture.SetupDescription = "Set the numeric format to " + ...
                fixture.Format + ".";
            fixture.TeardownDescription =  ...
                "Restored the numeric format to " + ...
                originalFormat.NumericFormat + ".";
        end
    end

    methods (Access=protected)
        function tf = isCompatible(fixture1,fixture2)
            tf = fixture1.Format == fixture2.Format;
        end
    end
end

Apply Custom Fixture to Single Test Class

In a file named ExampleTest.m in your current folder, create the ExampleTest class that applies the custom fixture and verifies that a numeric value is displayed in the expected format. To simplify this example, the actual value is produced by a call to the formattedDisplayText function. In practice, you test user-defined code.

classdef ExampleTest < matlab.unittest.TestCase
    methods (Test)
        function formatTest(testCase)
            testCase.applyFixture(NumericFormatFixture("bank"))
            actual = strtrim(formattedDisplayText(pi));
            expected = "3.14";
            testCase.verifyEqual(actual,expected)
        end
    end
end

Run the ExampleTest class. The testing framework sets up the fixture, which changes the display format to the currency format. Once the test run is complete, the framework tears down the fixture, which restores the original display format. In this example, the test passes.

runtests("ExampleTest");
Running ExampleTest
.
Done ExampleTest
__________

Apply Custom Fixture as Shared Fixture

In your current folder, create three test classes that each use an instance of NumericFormatFixture as a shared test fixture.

In a file named TestA.m, create the TestA class.

classdef (SharedTestFixtures={NumericFormatFixture("bank")}) ...
        TestA < matlab.unittest.TestCase
    methods (Test)
        function formatTest(testCase)
            actual = strtrim(formattedDisplayText(pi));
            expected = "3.14";
            testCase.verifyEqual(actual,expected)
        end
    end
end

In a file named TestB.m, create the TestB class.

classdef (SharedTestFixtures={NumericFormatFixture("bank")}) ...
        TestB < matlab.unittest.TestCase
    methods (Test)
        function formatTest(testCase)
            actual = strtrim(formattedDisplayText(100/3));
            expected = "33.33";
            testCase.verifyEqual(actual,expected)
        end
    end
end

In a file named TestC.m, create the TestC class.

classdef (SharedTestFixtures={NumericFormatFixture("hex")}) ...
        TestC < matlab.unittest.TestCase
    methods (Test)
        function formatTest(testCase)
            actual = strtrim(formattedDisplayText(1));
            expected = "3ff0000000000000";
            testCase.verifyEqual(actual,expected)
        end
    end
end

The TestA and TestB classes are assigned shared fixtures that make the same change to the environment. On the other hand, the TestC class is assigned a fixture that enforces a different numeric format. According to the implementation of the isCompatible method in this example, the testing framework finds the fixtures on TestA and TestB compatible. However, it finds the fixture on TestC incompatible with the other fixtures.

The information about fixture compatibility helps the framework decide when to perform teardown and setup actions. If you run TestA, TestB, and TestC as part of the same test suite, the framework does not tear down the fixture when switching from TestA to TestB because both classes require the same environment. However, when switching from TestB to TestC, the framework tears down the existing fixture and sets up a fresh fixture required by TestC. In this example, all the tests pass.

runtests(["TestA" "TestB" "TestC"]);
Setting up NumericFormatFixture
Done setting up NumericFormatFixture: Set the numeric format to bank.
__________

Running TestA
.
Done TestA
__________

Running TestB
.
Done TestB
__________

Tearing down NumericFormatFixture
Done tearing down NumericFormatFixture: Restored the numeric format to short.
__________

Setting up NumericFormatFixture
Done setting up NumericFormatFixture: Set the numeric format to hex.
__________

Running TestC
.
Done TestC
__________

Tearing down NumericFormatFixture
Done tearing down NumericFormatFixture: Restored the numeric format to short.
__________

Alternative Approach to Calling addTeardown in setup Method

An alternative approach to calling the addTeardown method within the setup method is to implement a separate teardown method. This code shows how to recreate the NumericFormatFixture class by implementing both the setup and teardown methods. Note that the alternative class definition contains an additional property, OriginalFormat, to pass information about the original format to the teardown method.

classdef NumericFormatFixture < matlab.unittest.fixtures.Fixture
    properties (SetAccess=immutable)
        Format (1,1) string
    end

    properties (Access=private)
        OriginalFormat
    end

    methods
        function fixture = NumericFormatFixture(fmt)  
            fixture.Format = fmt;
        end

        function setup(fixture)
            fixture.OriginalFormat = format().NumericFormat;
            format(fixture.Format)
            fixture.SetupDescription = "Set the numeric format to " + ...
                fixture.Format + ".";
        end

        function teardown(fixture)
            format(fixture.OriginalFormat)
            fixture.TeardownDescription =  ...
                "Restored the numeric format to " + ...
                fixture.OriginalFormat + ".";
        end
    end

    methods (Access=protected)
        function tf = isCompatible(fixture1,fixture2)
            tf = fixture1.Format == fixture2.Format;
        end
    end
end

See Also

| |

Related Topics