主要内容

Writing Tests Using Polyspace Test xUnit API for C/C++ Code

The Polyspace® Test™ xUnit API for C/C++ code uses macros to define tests and suites of tests. For example, the macro PST_SIMPLE_TEST used as:

PST_SIMPLE_TEST(test)  {
    /*test case body*/
}
defines a simple test that is not part of a suite. The macros for creating tests begin with PST_ (or pst_) and are defined in the header file pstunit.h, located in polyspaceroot\polyspace\pstest\pstunit\include. Here, polyspaceroot is the Polyspace installation folder, for example, C:\Program Files\Polyspace\R2026a. To use the macros, include this header file in your tests:
#include <pstunit.h>
You typically do not need to know the exact macro definitions in the header file. You can follow the syntax and usage examples in this documentation to use the macros. If you use the Polyspace as You Code™ extension in Visual Studio Code, you can also start typing PST_ to see autocomplete suggestions for macros from the C/C++ xUnit API. You can hover on a suggestion to see a description of the macro, and select a suggestion to enter the macro with placeholders to be filled in.

The sections below show the various strategies for authoring tests for C/C++ code. You can mix the strategies described below. For instance, you can author a suite of tests with a one-time setup and teardown before the suite and also add test-specific setup and teardown for a particular test in the suite.

Prerequisites

This topic describes test authoring using the Polyspace Test xUnit API. To compile these tests, you are required to know some file paths in advance. For your convenience, you can define environment variables to stand for the file paths, or otherwise include the file paths in your build. For more information, see Set Up C/C++ Testing and Code Profiling Using Self-Managed Builds.

Write Simple Test

Write a simple test that is not part of a suite:

/* Write test */
PST_SIMPLE_TEST(test)  {
    /* Test case body */
}

/* Register test - not needed in C++*/
PST_REGFCN(register_function) {
    PST_ADD_SIMPLE_TEST(test);
}

/* Call registered tests - not needed in C++*/
int main(int argc, char* argv[]) {
    PST_REGFCN_CALL(register_function);
    return PST_MAIN(argc, argv);
}
For more information, see Write C/C++ Unit Tests Using Polyspace Test xUnit API and Run Tests at Command Line.

Write Simple Test With Setup and Teardown

Write a simple test with test setup and teardown:

/* Define setup and teardown functions */
void setup_function(void) {
   /* Setup function body */
}

void teardown_function(void) {
   /* Teardown function body */
}

/* Write test configuration with setup and teardown */
PST_SIMPLE_TEST_CONFIG(test)  {
    PST_SETUP(setup_function);
    PST_TEARDOWN(teardown_function);
}

/* Write test body */
PST_SIMPLE_TEST_BODY(test)  {
    /*test case body*/
}

/* Register test - not needed in C++*/
PST_REGFCN(register_function) {
    PST_ADD_SIMPLE_TEST(test);
}

/* Call registered tests - not needed in C++*/
int main(int argc, char* argv[]) {
    PST_REGFCN_CALL(register_function);
    return PST_MAIN(argc, argv);
}

Write Suite of Tests With One-Time Setup and Teardown

Write a suite of tests with:

  • A setup function that runs once before all tests.

  • A teardown function that runs after all tests.

Use the setup and teardown function to ensure that relevant global data are in a known state when the suite begins and ends execution.

/* Define setup and teardown functions */
void suite_setup_function(void) {
   /* Setup function body */
}

void suite_teardown_function(void) {
   /* Teardown function body */
}

/* Write suite configuration */
PST_SUITE_CONFIG(suite) {
    PST_SUITE_SETUP(suite_setup_function);
    PST_SUITE_TEARDOWN(suite_teardown_function);
}

/* Write test 1*/
PST_TEST(suite, test_1) {
    /* Test 1 body */
}

/* Write test 2*/
PST_TEST(suite, test_2) {
    /* Test 2 body */
}

/* Register tests - not needed in C++ */
PST_REGFCN(register_function) {
    PST_ADD_TEST(suite, test_1);
    PST_ADD_TEST(suite, test_2);
}

/* Call registered tests - not needed in C++*/
int main(int argc, char* argv[]) {
    PST_REGFCN_CALL(register_function);
    return PST_MAIN(argc, argv);
}

The bodies of the setup/teardown functions and the tests execute in this order:

suite_setup_function runs first, then test_1, then test_2. then suite_teardown_function

For more information, see Group C/C++ Tests into Suites with Common Setup and Teardown Code.

In C++, you can alternatively define the suite fixture data and methods as a class. A suite fixture class inherits from the class pst_suite_fixture and defines the methods suite_setup() and suite_teardown() in the suite fixture class for setup and teardown. These methods are automatically invoked prior to running the suite. If you define a data member in the suite fixture class, you can access the member using the macro PST_SUITE_FIXTURE_PTR.

/* Define suite setup and teardown */
class suite_fixture : public pst_suite_fixture { 
  public:
    static void suite_setup() 
       {/* suite setup body */}
    static void suite_teardown() 
       {/* suite teardown body */}
    int a_suite_fixture_data_member;
}

PST_SUITE_WITH_FIXTURE(suite, suite_fixture);
/* suite pst_default_suite is reserved */

/* Write test 1*/
PST_TEST(suite, test_1) {
    int suite_fixture_data = PST_SUITE_FIXTURE_PTR() -> a_suite_fixture_data_member;
    /* Test 1 body */
}

/* Write test 2*/
PST_TEST(suite, test_2) {
    int suite_fixture_data = PST_SUITE_FIXTURE_PTR() -> a_suite_fixture_data_member;
    /* Test 2 body */
}

/* You do not need explicit test registration in C++*/
For more information, see Write Test Fixtures Using Polyspace Test C++ API.

Write Suite of Tests With Same Setup and Teardown Before Each Test

Write a suite of tests with:

  • A setup function that runs once before each test.

  • A teardown function that runs after each test.

Use the setup and teardown function to ensure that relevant global data are in a known state when each test in the suite begins and ends execution.

/* Define setup and teardown functions */
void suite_test_setup_function(void) {
   /* Setup function body */
}

void suite_test_teardown_function(void) {
   /* Teardown function body */
}

/* Write suite configuration */
PST_SUITE_CONFIG(suite) {
    PST_SUITE_TEST_SETUP(suite_test_setup_function);
    PST_SUITE_TEST_TEARDOWN(suite_test_teardown_function);
}


/* Write test 1 */
PST_TEST(suite, test_1) {
    /* Test 1 body */
}

/* Write test 2 */
PST_TEST(suite, test_2) {
    /* Test 2 body */
}

/* Register tests - not needed in C++ */
PST_REGFCN(register_function) {
    PST_ADD_TEST(suite, test_1);
    PST_ADD_TEST(suite, test_2);
}

/* Call registered tests - not needed in C++*/
int main(int argc, char* argv[]) {
    PST_REGFCN_CALL(register_function);
    return PST_MAIN(argc, argv);
}

The bodies of the setup/teardown functions and the tests execute in this order:

suite_test_setup_function runs first, then test_1, then suite_test_teardown_function, then suite_test_setup_function again, then test_2 and finally suite_test_teardown function.

For more information, see Group C/C++ Tests into Suites with Common Setup and Teardown Code.

In C++, define the suite fixture data and methods as a class. A suite fixture class inherits from the class pst_suite_fixture and defines the methods test_setup() and test_teardown() in the suite fixture class for per-test setup and teardown. These methods are automatically invoked prior to running each test in the suite. If you define a data member in the suite fixture class, you can access the member using the macro PST_SUITE_TEST_FIXTURE_PTR.

/* Define suite setup and teardown */
class suite_fixture : public pst_suite_fixture {
  public:
    void test_setup() 
        {/* setup body */}
    void test_teardown() 
        {/* teardown body */}
    int a_test_fixture_data_member;
}

PST_SUITE_WITH_FIXTURE(suite, suite_fixture);
/* suite pst_default_suite is reserved */


/* Write test 1*/
PST_TEST(suite, test_1) {
    int test_fixture_data = PST_SUITE_TEST_FIXTURE_PTR() -> a_test_fixture_data_member;
    /* Test 1 body */
}

/* Write test 2*/
PST_TEST(suite, test_2) {
    int test_fixture_data = PST_SUITE_TEST_FIXTURE_PTR() -> a_test_fixture_data_member;
    /* Test 2 body */
}

/* You do not need explicit test registration in C++*/
For more information, see Write Test Fixtures Using Polyspace Test C++ API.

Write Suite of Tests With Different Setup and Teardown Before Each Test

Write a suite of tests that requires a different setup and teardown before each test. Use a common setup and teardown function for any steps that are common to all tests, and a test-specific setup and teardown function for steps that are specific to a particular test.

/* Define setup and teardown function common to all tests in suite */
void suite_test_setup_function(void) {
   /* Setup function body */
}

void suite_test_teardown_function(void) {
   /* Teardown function body */
}

/* Write suite configuration */
PST_SUITE_CONFIG(suite) {
    PST_SUITE_TEST_SETUP(suite_test_setup_function);
    PST_SUITE_TEST_TEARDOWN(suite_test_teardown_function);
}

/* Define test 1 setup and teardown functions */
void setup_function_1(void) {
   /* Setup function body */
}

void teardown_function_1(void) {
   /* Teardown function body */
}

/* Write test 1 cconfiguration*/
PST_TEST_CONFIG(suite, test_1) {
    PST_SETUP(setup_function_1);
    PST_TEARDOWN(teardown_function_1);
}

/* Write test 1 body */
PST_TEST_BODY(suite, test_1) {
    /* Test 1 body */
}

/* Define test 2 setup and teardown functions */
void setup_function_2(void) {
   /* Setup function body */
}

void teardown_function_2(void) {
   /* Teardown function body */
}

/* Write test 2 cconfiguration*/
PST_TEST_CONFIG(suite, test_2) {
    PST_SETUP(setup_function_2);
    PST_TEARDOWN(teardown_function_2);
}

/* Write test 2 body */
PST_TEST_BODY(suite, test_2) {
    /* Test 2 body */
}

/* Register tests - not needed in C++ */
PST_REGFCN(register_function) {
    PST_ADD_TEST(suite, test_1);
    PST_ADD_TEST(suite, test_2);
}

/* Call registered tests - not needed in C++*/
int main(int argc, char* argv[]) {
    PST_REGFCN_CALL(register_function);
    return PST_MAIN(argc, argv);
}

The bodies of the setup/teardown functions and the tests execute in this order:

suite_test_setup_function runs first, then setup_function_1, then test_1, then teardown_function_1, then suite_test_teardown_function. This repeats for test_2.

For more information, see Group C/C++ Tests into Suites with Common Setup and Teardown Code.

In C++, you can alternatively define the test-specific fixture data as a class. A test fixture class inherits from the class pst_test_fixture and defines the methods setup() and teardown() in the test fixture class for setup and teardown. These methods are automatically invoked prior to running each test in the suite. If you define a data member in the test fixture class, you can access the member using the macro PST_TEST_FIXTURE_PTR.

/* Define suite setup and teardown common to all tests in suite*/
class suite_fixture : public pst_suite_fixture {
  public:
    void test_setup() 
        {/* setup body */}
    void test_teardown() 
        {/* teardown body */}
    int a_test_fixture_data_member;
}

PST_SUITE_WITH_FIXTURE(suite, suite_fixture);
/* suite pst_default_suite is reserved */

/* Define test 1 setup and teardown */
class test_1_fixture : public pst_test_fixture {
  public:
    void setup() {/* setup body */}
    void teardown() {/* teardown body */}
    int a_test_1_fixture_data_member;
};

/* Define test 2 setup and teardown */
class test_2_fixture : public pst_test_fixture {
  public:
    void setup() {/* setup body */}
    void teardown() {/* teardown body */} 
    int a_test_2_fixture_data_member;
};

/* Write test 1*/
PST_TEST_WITH_FIXTURE(suite, test_1, test_1_fixture) {
    int test_common_fixture_data = PST_SUITE_TEST_FIXTURE_PTR() -> a_test_fixture_data_member;
    int test_1_fixture_data = PST_TEST_FIXTURE_PTR() -> a_test_1_fixture_data_member;
    /* test body */
}

/* Write test 2*/
PST_TEST_WITH_FIXTURE(suite, test_2, test_2_fixture) { 
    int test_common_fixture_data = PST_SUITE_TEST_FIXTURE_PTR() -> a_test_fixture_data_member;
    int test_2_fixture_data = PST_TEST_FIXTURE_PTR() -> a_test_2_fixture_data_member;
    /* test body */
}

/* You do not need explicit test registration in C++*/
For more information, see Write Test Fixtures Using Polyspace Test C++ API.

Write Tests with Known Failures

You can create tests for work in progress by using macros that encapsulate undefined functions or functions that are not fully implemented and therefore known to fail test requirements. You can enable these tests by defining the macro PST_ENABLE_EXPECTED_FAILURES at build time.

  • To define a test that is known to fail, use the macro PST_EXPECT_FAIL() in the test body, for example:

    PST_TEST(suite, test) {
        PST_EXPECT_FAIL();
        /* test body */
    }
    This macro causes the test to pass as long as there is a failing assessment in the test.

  • To define a test for an undefined function, you can encapsulate the test body inside a PST_TEST_NOT_IMPLEMENTED() macro, for example:

    PST_TEST(suite, test)
    PST_TEST_NOT_IMPLEMENTED(({
        /* test body that does not compile */
    }))
    When you execute the test, you see passing test results despite the presence of failed assertions, and additional messages stating that the test is not implemented. Once the function is defined, you can simply remove the encapsulation and thereby reenable the test.

For more information, see Write C/C++ Tests with Known Errors or Failures to Support Test-Driven Development.

See Also

Topics