Blog Datasheets Home About me Clients My work Services Contact

G2Labs Grzegorz Grzęda

Testing strategies for clean code in C and Python

June 27, 2023

Testing strategies for clean code in C and Python

Testing is an integral part of the software development process, and it plays a crucial role in ensuring that our code is clean, robust, and maintainable. In this blog post, we will explore testing strategies for writing clean code in C and Python, with extensive examples in both languages.

Why testing is important for clean code

Before we delve into testing strategies, let’s first understand why testing is important for writing clean code. Effective testing can help in the following ways:

  1. Bug detection: Testing helps in detecting and fixing bugs early in the development process, ensuring that the code is clean and free from errors.

  2. Refactoring confidence: When refactoring code to make it cleaner and more maintainable, having a comprehensive test suite provides confidence that the behavior of the code has not been altered.

  3. Documentation: Tests serve as living documentation for the codebase, providing insights into the expected behavior of the code.

  4. Code maintainability: Clean code is easier to maintain, and a solid test suite can prevent regressions as the codebase evolves.

Testing strategies in C

C is a low-level programming language, and testing in C can be challenging compared to high-level languages like Python. However, there are several strategies that can help in writing clean and testable code in C.

Unit testing with CMocka

To write clean and testable code in C, unit testing plays a crucial role. CMocka is a powerful unit testing framework for C that provides assertions, test fixtures, and test runners. Let’s consider a simple example of a function to calculate the square of a number and its corresponding unit tests using CMocka:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// square.c
#include "square.h"

int square(int num) {
    return num * num;
}

// square_test.c
#include <stdarg.h>
#include <stddef.h>
#include <setjmp.h>
#include <cmocka.h>
#include "square.h"

void test_square(void **state) {
    assert_int_equal(square(2), 4);
    assert_int_equal(square(3), 9);
    assert_int_equal(square(0), 0);
}

int main(void) {
    const struct CMUnitTest tests[] = {
        cmocka_unit_test(test_square),
    };
    return cmocka_run_group_tests(tests, NULL, NULL);
}

In this example, we have a simple square function and its corresponding unit tests using CMocka. This approach ensures that the code is clean, testable, and maintainable in C.

Mocking using CMocka

When writing clean and testable code in C, it’s essential to mock external dependencies for thorough testing. CMocka provides support for mocking external interfaces, allowing clean separation of concerns in the codebase. Here’s an example of mocking using CMocka:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// network.c
#include "network.h"

int send_data(const char *data) {
    // Implementation to send data over the network
}

// network_test.c
#include <stdarg.h>
#include <stddef.h>
#include <setjmp.h>
#include <cmocka.h>
#include "network.h"

void test_send_data(void **state) {
    expect_string(send_data, data, "test data");
    will_return(send_data, 1);
    assert_int_equal(send_data("test data"), 1);
}

int main(void) {
    const struct CMUnitTest tests[] = {
        cmocka_unit_test(test_send_data),
    };
    return cmocka_run_group_tests(tests, NULL, NULL);
}

In this example, we have a network module with a function to send data over the network, and its corresponding unit tests using CMocka with mocked external dependencies.

Testing strategies in Python

Python is well-known for its simplicity and readability, making it easier to write clean and testable code. Let’s explore testing strategies for clean code in Python with extensive examples.

Unit testing with unittest

Python’s unittest module provides a built-in framework for writing unit tests, making it easy to write clean and testable code. Here’s an example of a simple function to calculate the factorial of a number and its corresponding unit tests using unittest:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
# factorial.py
def factorial(n):
    if n == 0:
        return 1
    return n * factorial(n - 1)

# test_factorial.py
import unittest
from factorial import factorial

class TestFactorial(unittest.TestCase):
    def test_factorial(self):
        self.assertEqual(factorial(0), 1)
        self.assertEqual(factorial(1), 1)
        self.assertEqual(factorial(5), 120)

if __name__ == '__main__':
    unittest.main()

In this example, we have a simple factorial function and its corresponding unit tests using the unittest module. This approach ensures clean, testable, and maintainable code in Python.

Mocking using unittest.mock

When writing clean and testable code in Python, mocking external dependencies is essential for thorough testing. The unittest.mock module provides support for mocking external interfaces, allowing clean separation of concerns in the codebase. Here’s an example of mocking using unittest.mock:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
# network.py
import requests

def get_data():
    response = requests.get('https://api.example.com/data')
    return response.json()

# test_network.py
import unittest
from unittest.mock import patch
from network import get_data

class TestNetwork(unittest.TestCase):
    @patch('network.requests.get')
    def test_get_data(self, mock_get):
        mock_get.return_value.json.return_value = {'key': 'value'}
        self.assertEqual(get_data(), {'key': 'value'})

if __name__ == '__main__':
    unittest.main()

In this example, we have a network module with a function to fetch data from an external API and its corresponding unit tests using unittest.mock for mocking external dependencies.

Conclusion

In this blog post, we explored testing strategies for writing clean code in C and Python, with extensive examples in both languages. Effective testing plays a crucial role in ensuring that our code is clean, robust, and maintainable. By following the testing strategies outlined in this post, you can write clean, testable, and maintainable code in both C and Python.


➡️ The memcpy() function in C: A beginner's guide


⬅️ libc - limits header


Go back to Posts.