Main Content

matlab.test.behavior.Missing Class

Namespace: matlab.test.behavior
Superclasses: matlab.unittest.TestCase

Test if class satisfies contract for missing values

Description

To test if the missing value for your class satisfies the missing contract in MATLAB®, create a test class that derives from the matlab.test.behavior.Missing class. If your class represents a data type and you want MATLAB to treat missing values of your class similarly to built-in classes, make sure that your class satisfies the missing contract.

Typically, you use the behavior test as part of a test-driven development workflow. If you want the missing value for your class to satisfy the missing contract in MATLAB, write the behavior test and modify the class under test until the tests pass. For example, if your class supports comparisons and ordering and is usable as a missing value indicator, all tests should pass. If your class does not support ordering, set the SupportsOrdering property to false so that MATLAB filters out tests associated with ordering.

Your behavior test must derive only from matlab.test.behavior.Missing and define the abstract properties. However, because matlab.test.behavior.Missing is a subclass of matlab.unittest.TestCase, you can use the features of the unit testing framework.

Class Attributes

Abstract
true
HandleCompatible
true

For information on class attributes, see Class Attributes.

Properties

expand all

Define values for all abstract properties in the properties block of your test class.

Missing value for the class under test, specified as a valid MATLAB scalar value or an expression that returns a missing value.

Example: NaN

Example: missing

Example: ' '

Attributes:

SetAccess
public
GetAccess
public
Abstract
true

Any nonmissing value for the class under test, specified as a valid MATLAB scalar value or an expression that returns a nonmissing value. Typically, if the class constructor returns a nonmissing value, PrototypeValue is a call to the constructor.

Example: 0

Example: datetime

Attributes:

SetAccess
public
GetAccess
public
Abstract
true

Classes that can be converted to the class under test, specified as a string vector of class names.

If you can convert your class to the other class and back, then your class has a supported conversion from the other class. For example, if MyClass(OtherClass(missing)) == MyClass(missing), then OtherClass is a class with supported conversions.

Example: "string"

Example: ["double" "single"]

Attributes:

SetAccess
public
GetAccess
public
Abstract
true

If necessary, redefine values for concrete properties in the TestClassSetup methods block of your test class.

Whether the class supports comparison, returned as a 1 or 0 of data type logical. A class that supports comparison allows the use of == and ~=.

If you set SupportsComparison to false, then MATLAB filters out comparison and ordering tests by assumption failure. MATLAB does not run the tests.

Attributes:

SetAccess
protected
GetAccess
public

Whether the class supports ordering, returned as a 1 or 0 of data type logical. A class that supports ordering allows the use of <, >, <=, and >=.

If you set SupportsOrdering to false, then MATLAB filters out ordering tests by assumption failure. MATLAB does not run the tests.

Attributes:

SetAccess
protected
GetAccess
public

Whether the class is usable as a missing value indicator with the ismissing function, returned as a 1 or 0 of data type logical.

If you set UsableAsMissingIndicator to false, then MATLAB filters out associated tests by assumption failure. MATLAB does not run the tests.

Attributes:

SetAccess
protected
GetAccess
public

Fill value that the class uses for growing arrays, returned as a MATLAB scalar value. By default, the value of FillValue is the same as the value of the MissingValue property.

Example: 0

Attributes:

SetAccess
protected
GetAccess
public

Since R2023b

Class constructor arguments that result in incompatible values, returned as a cell vector. Use this property if the class under test has such constructor arguments. For example, the datetime class supports two categories, zoned and unzoned values, corresponding to whether the TimeZone name-value argument of the class constructor is specified. These values are incompatible; for instance, you cannot concatenate or compare zoned and unzoned values. To test against the arguments that result in incompatible values, set the ExtraConstructorArguments property.

If you set the ExtraConstructorArguments property, the values you assign to the MissingValue and PrototypeValue properties must use the arguments specified in ExtraConstructorArguments. For an example of how to test using the ExtraConstructorArguments property, see Write Behavior Tests for datetime Class.

Example: {"TimeZone","Asia/Tokyo"}

Attributes:

SetAccess
protected
GetAccess
public

Examples

collapse all

In your current folder, create a class named MyDataClass that supports a missing value. If you call the constructor with no inputs, it returns a missing value.

classdef MyDataClass
    properties
        SomeData;
        MissingVal = false;
    end
    
    methods
        function obj = MyDataClass(value)
            if nargin
                m = size(value,1);
                n = size(value,2);
                for i = 1:m
                    for j = 1:n
                        if ismissing(value(i,j))
                            obj(i,j).MissingVal = true;
                        else
                            obj(i,j).SomeData = value(i,j);
                            obj(i,j).MissingVal = false;
                        end
                    end
                end
            else
                obj.MissingVal = true;
            end
        end
        
        % Define ismissing behavior
        function m = ismissing(obj,v)
            if nargin > 1
                m = isequaln(obj,v);
            else
                m = [obj.MissingVal];
            end
            m = reshape(m,size(obj));
        end
    end
end

To create a simple test class that checks that MyDataClass satisfies the missing contract, subclass matlab.test.behavior.Missing and define the abstract properties. The test can use the features of the unit testing framework, but MissingValueTest checks the missing contract only.

classdef MissingValueTest < matlab.test.behavior.Missing
    properties
        MissingValue = MyDataClass;
        PrototypeValue = MyDataClass(7);
        ClassesWithSupportedConversions = [];
    end
end

Run the tests and review the results. The tests for comparison, ordering, equality, and using MyDataClass as the second input to ismissing fail.

results = runtests("MissingValueTest");
Running MissingValueTest
....
================================================================================
Error occurred in MissingValueTest/comparison and it did not run to completion.

    ---------
    Error ID:
    ---------
    'MATLAB:UndefinedFunction'

    --------------
    Error Details:
    --------------
    Undefined function 'eq' for input arguments of type 'MyDataClass'.
    
    Error in matlab.test.behavior.Missing/comparison (line 129)
                testCase.verifyFalse(testCase.MissingValue == testCase.MissingValue,
                getString(message('MATLAB:test:behavior:missing:EqualFalse')));
================================================================================
.
================================================================================
Error occurred in MissingValueTest/ordering and it did not run to completion.

    ---------
    Error ID:
    ---------
    'MATLAB:UndefinedFunction'

    --------------
    Error Details:
    --------------
    Undefined function 'lt' for input arguments of type 'MyDataClass'.
    
    Error in matlab.test.behavior.Missing/ordering (line 136)
                testCase.verifyFalse(testCase.MissingValue < testCase.MissingValue,
                getString(message('MATLAB:test:behavior:missing:LessThanFalse')));
================================================================================
.
================================================================================
Verification failed in MissingValueTest/isequalRules.

    ----------------
    Test Diagnostic:
    ----------------
    isequal(MissingValue, MissingValue) must return false, because all missing values are unequal.

    ---------------------
    Framework Diagnostic:
    ---------------------
    verifyFalse failed.
    --> The value must evaluate to "false".
    
    Actual Value:
      logical
    
       1

    ------------------
    Stack Information:
    ------------------
    In <matlabroot>\toolbox\matlab\datatypes\+matlab\+test\+behavior\Missing.m (Missing.isequalRules) at 145
================================================================================

================================================================================
Verification failed in MissingValueTest/isequalRules.

    ----------------
    Test Diagnostic:
    ----------------
    isequaln(MissingValue, missing) must return true.

    ---------------------
    Framework Diagnostic:
    ---------------------
    verifyTrue failed.
    --> The value must evaluate to "true".
    
    Actual Value:
      logical
    
       0

    ------------------
    Stack Information:
    ------------------
    In <matlabroot>\toolbox\matlab\datatypes\+matlab\+test\+behavior\Missing.m (Missing.isequalRules) at 147
================================================================================
.
================================================================================
Verification failed in MissingValueTest/IsMissing2ndInput.

    ----------------
    Test Diagnostic:
    ----------------
    ismissing(MissingValue, missing) must return true.

    ---------------------
    Framework Diagnostic:
    ---------------------
    verifyTrue failed.
    --> The value must evaluate to "true".
    
    Actual Value:
      logical
    
       0

    ------------------
    Stack Information:
    ------------------
    In <matlabroot>\toolbox\matlab\datatypes\+matlab\+test\+behavior\Missing.m (Missing.IsMissing2ndInput) at 154
================================================================================
...
Done MissingValueTest
__________

Failure Summary:

     Name                                Failed  Incomplete  Reason(s)
    =================================================================================
     MissingValueTest/comparison           X         X       Errored.
    ---------------------------------------------------------------------------------
     MissingValueTest/ordering             X         X       Errored.
    ---------------------------------------------------------------------------------
     MissingValueTest/isequalRules         X                 Failed by verification.
    ---------------------------------------------------------------------------------
     MissingValueTest/IsMissing2ndInput    X                 Failed by verification.

Iteratively update MyDataClass to satisfy the missing contract. To satisfy comparison and ordering, define eq, ne, lt, gt, le, and ge in the methods block of MyDataClass.

        % Class supports comparison
        function tf = eq(obj1,obj2)
            tf = ~any(ismissing([obj1 obj2])) && eq(obj1.SomeData,obj2.SomeData);
        end
        function tf = ne(obj1,obj2)
            tf = ~eq(obj1,obj2);
        end
        
        % Class supports ordering
        function tf = lt(obj1,obj2)
            tf = ~any(ismissing([obj1 obj2])) && lt(obj1.SomeData,obj2.SomeData);
        end
        function tf = gt(obj1,obj2)
            tf = lt(obj2,obj1);
        end   
        function tf = le(obj1,obj2)
            tf = ~any(ismissing([obj1 obj2])) && ~gt(obj1,obj2);
        end
        function tf = ge(obj1,obj2)
            tf = le(obj2,obj1);
        end

Run the tests with a terse level of output detail and review the results.

results = runtests("MissingValueTest","OutputDetail",1);
......
FAIL: MissingValueTest/isequalRules in Missing.isequalRules at 145 :: verifyFalse failed.

FAIL: MissingValueTest/isequalRules in Missing.isequalRules at 147 :: verifyTrue failed.
.
FAIL: MissingValueTest/IsMissing2ndInput in Missing.IsMissing2ndInput at 154 :: verifyTrue failed.
...

Iteratively update MyDataClass to satisfy the equality rules and be accepted as the second input to the ismissing function. Define isequal and isequaln in the methods block of MyDataClass.

        % Class supports isequal/isequaln rules
        function tf = isequal(obj1,obj2)
            tf = eq(obj1,obj2);
        end
        function tf = isequaln(obj1,obj2)
            tf = all(ismissing([obj1 obj2])) || eq(obj1,obj2);
        end

Run the tests and review the results. The tests pass, so MyDataClass satisfies the missing value contract.

results = runtests("MissingValueTest");
Running MissingValueTest
..........
Done MissingValueTest
__________

Write tests to verify that the datetime class satisfies the missing contract in MATLAB for both zoned and unzoned datetime values. A zoned datetime value is a value you create by specifying the TimeZone name-value argument during creation. This example is for illustration purposes only. In practice, you test user-defined classes.

In your current folder, create the MissingDatetimeTest test class by subclassing matlab.test.behavior.Missing. Define the abstract properties that the class inherits in a properties block. Note that the MissingDatetimeTest class contains unzoned datetime values (that is, datetime and NaT).

classdef MissingDatetimeTest < matlab.test.behavior.Missing
    properties
        MissingValue = NaT
        PrototypeValue = datetime
        ClassesWithSupportedConversions = "string"
    end
end

Run the MissingDatetimeTest test class. The tests pass, which indicates that the datetime class satisfies the missing contract for unzoned datetime values.

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

datetime values that you create with the TimeZone name-value argument are not compatible with unzoned values. For example, you cannot concatenate zoned and unzoned datetime values. Because the TimeZone constructor argument results in incompatible values, create another behavior test to make sure that the datetime class satisfies the missing contract for zoned values as well. In your current folder, create the ZonedMissingDatetimeTest test class by subclassing matlab.test.behavior.Missing. Define the abstract properties, and additionally specify the constructor argument to test against by setting the ExtraConstructorArguments property in a TestClassSetup methods block. The values you assign to the MissingValue and PrototypeValue properties must use the specified name-value argument in ExtraConstructorArguments.

classdef ZonedMissingDatetimeTest < matlab.test.behavior.Missing
    properties
        MissingValue = NaT(TimeZone="Asia/Tokyo")
        PrototypeValue = datetime(2023,6,23,TimeZone="Asia/Tokyo")
        ClassesWithSupportedConversions = "string"
    end

    methods (TestClassSetup)
        function zonedDatetimeConstructorArgs(testCase)
            testCase.ExtraConstructorArguments = {"TimeZone","Asia/Tokyo"};
        end
    end
end

Run the ZonedMissingDatetimeTest test class. The tests pass, which indicates that the datetime class satisfies the missing contract for zoned datetime values as well.

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

More About

expand all

Version History

Introduced in R2018b

expand all

See Also

Functions

Classes