Embedded Software Design for Testability

Star Public Account“, Let’s Progress Together!Embedded Software Design for Testability

Hello everyone, I am Mixed Cuisine Jun.

This time we will introduce embedded software design for testability.

What is testability? It means that after you finish writing a software module/function interface, you can conveniently and comprehensively perform self-testing.

Let’s take a simple example to understand testable software.

There is a calculation function cal_func, which depends on data a in flash memory and an external input data b.

At this point, there are two implementation methods:

Method One:

int get_a_from_flash(void)
{
 int a = 0;
 flash_read(&a, sizeof(int));
 
 return a;
}

int cal_func(int b)
{
 int res = 0;
 int a = get_a_from_flash();
 
 res = a + b;
 
 return res;
}

// Call
cal_func(5);

Method Two:

int get_a_from_flash(void)
{
 int a = 0;
 flash_read(&a, sizeof(int));
 
 return a;
}

int cal_func(int a, int b)
{
 int res = 0;
 
 res = a + b;
 
 return res;
}

// Call
cal_func(get_a_from_flash(), 5);

Such scenarios are quite common in actual development. Do you usually write code according to Method One or Method Two?

From the perspective of testability, Method Two's implementation is more testable.

Method One has a data that is read from flash within the function, so we find it inconvenient to control that data, while we can only control parameter b. Thus, when we call for testing, we might not get comprehensive results and cannot flexibly control the testing paths.

Method Two leaves all the data dependencies through function parameters, allowing us to easily test the function and input different data combinations.

Moreover, generally, we will introduce some unit testing frameworks to uniformly manage our test cases.

Common testing frameworks in embedded systems:

  • Unity: https://github.com/ThrowTheSwitch/Unity/releases
  • cutest: https://sourceforge.net/projects/cutest/
  • embunit: https://sourceforge.net/projects/embunit
  • googletest: https://github.com/google/googletest/releases

After using the testing framework, the test code designed for cal_func function is as follows:

int ut_cal_func(int argc, char *argv[])
{
    if (argc != 3)
    {
        printf("Param num err\n");
        return USAGE;
    }

    // Expected result
    int expected_res = atoi(argv[2]); 
    // Actual result                  
 int res = cal_func(atoi(argv[0]), atoi(argv[1]));   

    if (expected_res == res)
    {
        printf("input %d, %d, test pass!\n", atoi(argv[0]), atoi(argv[1]));
    }
    else
    {
        printf("input %d, %d, test failed!\n", atoi(argv[0]), atoi(argv[1]));
    }

 return 0;
}

We encapsulate it into serial port testing commands:

// Test Path 1
ut app ut_cal_func 1 2 3
    
// Test Path 2
ut app ut_cal_func 2 3 5
    
// ...

Output:

input 1, 2, test pass!
input 2, 3, test pass!

This is a small example of testable software design. Through this small example, everyone should realize the benefits of testable software, right?

Therefore, when writing code in the future, it is necessary to think clearly about how this module will be self-tested and what areas need to be tested.

Designing software with strong testability allows us to conduct thorough testing during the development phase, solving as many logical issues as possible, thereby ensuring higher quality software delivery.

This concludes our sharing this time. Feel free to bookmark and share!

Year-End Welfare Activity!

In embedded systems, what are the protocol compatibility issues involved during upgrades?

In embedded systems, what are some rules for log debugging?

Leave a Comment