Contenuto principale

Unit Test Generated Standalone Class

R2026b
Since R2026b

This example shows how to use MATLAB® unit tests to validate an entry-point class. When you write MATLAB code, you can create and run unit tests by using the MATLAB testing framework. You can use the same unit tests to test the generated MEX code. If you have an Embedded Coder® license, you can also use MATLAB unit tests in software-in-the-loop (SIL) or processor-in-the-loop execution to verify the standalone class in a separate process outside of MATLAB. See Code Verification Through Software-in-the-Loop and Processor-in-the-Loop Execution (Embedded Coder).

Note

Using a MATLAB class as an entry point for code generation is a tech preview. This feature is in active development and might change between the tech preview and the general release. To enable the feature, enter enableCodegenForEntryPointClasses at the command line before launching the MATLAB Coder™ app, calling the codegen function, or creating a coder.Type object. To provide feedback, email the development team or participate in a survey.

Unit Test Generated C++ Class with SIL Execution

In this example, you generate MEX code for an entry-point class and use a unit test to test the generated class. You then use verify the standalone C++ class in a separate process outside of MATLAB by using SIL.

Enable Tech Preview

Before you begin, enable code generation for entry-point classes.

enableCodegenForEntryPointClasses
=== Code generation for entry-point classes is ENABLED ===

To use the Classes As Entry-Points feature, restart MATLAB and rerun 'enableCodegenForEntryPointClasses' before calling codegen or coder.Type.

For feature overview and example usage, see Code Generation for Entry-Point Classes.

To send feedback or questions directly to the development team, email entrypointclassfeedback@groups.mathworks.com or click here to take survey.

Examine MATLAB Unit Tests

The example performs unit tests on the standalone C++ classes generated for simple and damped oscillators in the example Generate Standalone C++ Classes to Represent a Physical System.

Examine the MATLAB unit test, which instantiates and evolves each oscillator given the initial conditions described in the example. It then checks that the final position of each oscillator matches the expected value.

type tOscillators.m
classdef tOscillators < matlab.unittest.TestCase

    methods (Test)

        function matchesActualFinalPosition_simple(testCase)
            obj = mySystem.simpleOscillator(1,1);
            [~,x] = obj.evolution(1,0,100,0.1);
            testCase.verifyEqual(x(end),0.8623,"AbsTol",5e-4);
        end

        function matchesActualFinalPosition_damped(testCase)
            obj = mySystem.dampedOscillator(1,0.1,1);
            [~,x] = obj.evolution(1,0,100,0.1);
            testCase.verifyEqual(x(end),0.0056,"AbsTol",5e-4);
        end
    end
end

Run the unit tests on the MATLAB code. The tests pass.

runtests("tOscillators")
Running tOscillators
..
Done tOscillators
__________
ans = 
  1×2 TestResult array with properties:

    Name
    Passed
    Failed
    Incomplete
    Duration
    Details

Totals:
   2 Passed, 0 Failed, 0 Incomplete.
   0.27371 seconds testing time.

Specify Entry-Point Classes

Create coder.ClassSignature objects for the simple and damped oscillator classes. Use the addMethod object function to specify the public methods that you want to access from your external C++ application. For this example, specify the constructor methods and the evolution methods.

classSig_simple = coder.ClassSignature("mySystem.simpleOscillator");
addMethod(classSig_simple,"simpleOscillator",{0,0});
addMethod(classSig_simple,"evolution",{classSig_simple,0,0,0,0})
ans = 
coder.ClassSignature
  1×1 mySystem.simpleOscillator
    TypeName: "simpleOscillator"
    Properties: struct with no fields.
    Methods:
      simpleOscillator:
        Args: {1×1 double, 1×1 double}
      evolution:
        Args: {1×1 this, 1×1 double, 1×1 double, 1×1 double, 1×1 double}
classSig_damped = coder.ClassSignature("mySystem.dampedOscillator");
addMethod(classSig_damped,"dampedOscillator",{0,0,0});
addMethod(classSig_damped,"evolution",{classSig_damped,0,0,0,0})
ans = 
coder.ClassSignature
  1×1 mySystem.dampedOscillator
    TypeName: "dampedOscillator"
    Properties: struct with no fields.
    Methods:
      dampedOscillator:
        Args: {1×1 double, 1×1 double, 1×1 double}
      evolution:
        Args: {1×1 this, 1×1 double, 1×1 double, 1×1 double, 1×1 double}

Run Unit Tests on MEX Code

Generate MEX code from the entry-point classes and use the test script to test the MEX classes. Because the function coder.runTest does not support entry-point classes, manually modify the test script to call the MEX function.

By default, calling the codegen command for multiple entry-point classes generates a C MEX function with the same name as the first MATLAB class passed to the command. For this example, specify a different name for the generated MEX function by using the -o option. Use the -class option to specify each class object, and use the -lang:c++ option to generate C++ code.

codegen -lang:c++ -o oscillatorsMex -class classSig_simple -class classSig_damped
Code generation successful.

Modify the test script to use the MEX classes. To invoke the class constructor for each class, pass the name of the class constructor to the MEX function, followed by the required inputs. Because the classes are inside a namespace, call the constructor methods by using dot notation. For this example, create the class instances by using these lines of code:

obj = oscillatorsMex("mySystem.simpleOscillator",1,1);
obj = oscillatorsMex("mySystem.dampedOscillator",1,0.1,1);

The file tOscillatorsMex.m contains the updated code.

type tOscillatorsMex.m
classdef tOscillatorsMex < matlab.unittest.TestCase

    methods (Test)

        function matchesActualFinalPosition_simple(testCase)
            obj = oscillatorsMex("mySystem.simpleOscillator",1,1);
            [~,x] = obj.evolution(1,0,100,0.1);
            testCase.verifyEqual(x(end),0.8623,"AbsTol",5e-4);
        end

        function matchesActualFinalPosition_damped(testCase)
            obj = oscillatorsMex("mySystem.dampedOscillator",1,0.1,1);
            [~,x] = obj.evolution(1,0,100,0.1);
            testCase.verifyEqual(x(end),0.0056,"AbsTol",5e-4);
        end
    end
end

Run the unit tests on the MEX classes. The tests pass.

runtests("tOscillatorsMex.m")
Running tOscillatorsMex
..
Done tOscillatorsMex
__________
ans = 
  1×2 TestResult array with properties:

    Name
    Passed
    Failed
    Incomplete
    Duration
    Details

Totals:
   2 Passed, 0 Failed, 0 Incomplete.
   0.063422 seconds testing time.

Run Unit Tests with SIL Verification

Next, if you have an Embedded Coder license, run the unit tests on the generated standalone code by using SIL verification. Because the function coder.runTest does not support entry-point classes, manually modify the test script to call the SIL function.

Create a code configuration object for a static library. To generate code for SIL verification, set the VerificationMode property to "SIL". See Verification mode.

cfg = coder.config("lib");
cfg.VerificationMode = "SIL";

Generate a SIL function by using the codegen command. Specify the configuration object by using the -config option and specify a different name for the generated SIL function by using the -o option. Use the -lang:c++ option to generate C++ code and the -class option to specify each class object.

codegen -config cfg -lang:c++ -o oscillatorsSIL -class classSig_simple -class classSig_damped
Code generation successful.

Modify the test script to use SIL verification. To invoke the class constructor for each class, pass the name of the class constructor to the SIL function, followed by the required inputs. Because the classes are inside a namespace, call the constructor methods by using dot notation. For this example, create the class instances by using these lines of code:

obj = oscillatorsSIL_sil("mySystem.simpleOscillator",1,1);
obj = oscillatorsSIL_sil("mySystem.dampedOscillator",1,0.1,1);

The file tOscillatorsSIL.m contains the updated code.

type tOscillatorsSIL.m
classdef tOscillatorsSIL < matlab.unittest.TestCase

    methods (Test)

        function matchesActualFinalPosition_simple(testCase)
            obj = oscillatorsSIL_sil("mySystem.simpleOscillator",1,1);
            [~,x] = obj.evolution(1,0,100,0.1);
            testCase.verifyEqual(x(end),0.8623,"AbsTol",5e-4);
        end

        function matchesActualFinalPosition_damped(testCase)
            obj = oscillatorsSIL_sil("mySystem.dampedOscillator",1,0.1,1);
            [~,x] = obj.evolution(1,0,100,0.1);
            testCase.verifyEqual(x(end),0.0056,"AbsTol",5e-4);
        end
    end
end

Run the unit tests on the standalone C++ classes by using SIL. The tests pass.

runtests("tOscillatorsSIL.m")
Running tOscillatorsSIL
### Starting SIL execution for 'oscillatorsSIL'
    To terminate execution: clear oscillatorsSIL_sil
..
Done tOscillatorsSIL
__________
ans = 
  1×2 TestResult array with properties:

    Name
    Passed
    Failed
    Incomplete
    Duration
    Details

Totals:
   2 Passed, 0 Failed, 0 Incomplete.
   0.28266 seconds testing time.

Terminate SIL execution.

clear oscillatorsSIL_sil
### Application stopped
### Stopping SIL execution for 'oscillatorsSIL'

See Also

| | |

Topics