---
# System prepended metadata

title: 4/27 Test-Driven Development
tags: [NTURT, electrical_system, experience_sharing]

---

# 4/27 Test-Driven Development
###### tags: `experience_sharing` `electrical_system` `NTURT`
##### Author: @QuantumSpawner

## What is TDD

TDD stands for **test-driven development**. Which essentially means write tests before you even write any production code. And the development cycle looks like:

1. Add a small test.
2. Run all the tests and see the new one fail, maybe not even compile.
3. Make the small changes needed to pass the test.
4. Run all the tests and see the new one pass.
5. Refactor to remove duplication and improve expressiveness.

courtesy: Kent Beck. *Test Driven Development: By Example* 

By testing everytime you finish a new feature of the code, you're guaranteed that the code behaves as expected!

## Why should we use TDD

From *Test-Driven Development in Embedded C*:

> We need TDD because we’re human and we make mistakes. Computer programming is a very complex activity. Among other reasons, TDD is needed to systematically get our code working as intended and to produce the automated test cases that keep the code working.

Debug later programming:

![](https://i.imgur.com/Q6vhjKp.png)

courtesy: James W. Greening. *Test-Driven Development in Embedded C*, Fig 1.1, p. 29

Test-driven development:

![](https://i.imgur.com/lQ5zIcQ.png)

courtesy: James W. Greening. *Test-Driven Development in Embedded C*, Fig 1.2, p. 30

As the saying goes, you fix a bug while creating another. **The best practice is to find all the bugs using tests.**

## How to test

:::warning

Disclaimer: There're still other types of test then unit test. But since I'm only familiar with unit test, I would only introduce it here.

:::

### Terminologies

#### Testing

- Test harness / framework: The program that helps you write and run the tests.
- Test case: A test case is the most fundemental element of a unit test.
- Test fixture / context: A test fixture is the set of preconditions or state needed to run a test.
- Tese suit: A test suite is a set of tests that all share the same fixture (or they test similar functionality of the code).

> Note: Since most test frameworks nowaday are besd on xUnit, we will use it's terminologies here.

#### Mocking

- Dummy: Contains no implementation.
- Stub: Provide a valid response, but it's always the same.
- Fake: Act like the real object, but they go about it in a simpler way.

### Using GoogleTest

GoogleTest is one of the most known C/C++ test framework.

#### Writing a test

The following is a simple example how to write a unit test:

```cpp=
#include <gtest/gtest.h>

int add(const int a, const int b) {
    return a + b;
}

TEST(HelloGTest, Add) {
    EXPECT_EQ(add(1, 1), 2);
    EXPECT_EQ(add(1, 2), 3); 
}
```

#### Mocking dependencies

However, since it's almost always impossible to not rely on external dependencies in your code, no matter it's from a system or third party library. Hence we have to mock it. 

GoogleTest provides a powerful library GoogleMock for mocking dependencies, let's see a simple example:

```cpp=
#include <gmock/gmock.h>
#include <gtest/gtest.h>

class Interface {
 public:
  virtual ~Interface(){};

  virtual int add(const int a, const int b) const = 0;

  virtual int minus(const int a, const int b) const = 0;
};

class Client {
 public:
  Client(Interface *interface) : interface_(interface){};

  int add(const int a, const int b) const { return interface_->add(a, b); }

  int minus(const int a, const int b) const { return interface_->minus(a, b); }

 private:
  Interface *interface_;
};

class MockInterface : public Interface {
 public:
  MOCK_METHOD(int, add, (const int a, const int b), (const, override));

  MOCK_METHOD(int, minus, (const int a, const int b), (const, override));
};

TEST(HelloGMock, Add) {
  MockInterface mock_interface;
  EXPECT_CALL(mock_interface, add(1, 1)).WillOnce(testing::Return(2));

  Client clinet(&mock_interface);
  EXPECT_EQ(clinet.add(1, 1), 2);
}
```

As you may see, GoogleMock utilizes the polymorphism of class to mock the interface class `Interface` to replace the dependency by it's own.

#### Mocking c-style function

However, if your dependency is written in a c-style function (not as a class interface) or you're testing a c library , you have to use a plugin for GoogleMock `C-Mock`. 

This is a simple example how to use it:

```clike=
// c_library.h
#ifndef C_LIBRARY_H
#define C_LIBRARY_H

int add(const int a, const int b);

int minus(const int a, const int b);

#endif  // C_LIBRARY_H

```

```cpp=
#include <cmock/cmock.h>
#include <gtest/gtest.h>

#include "c_library.h"

class C_LibraryMock : public CMockMocker<C_LibraryMock> {
 public:
  CMOCK_MOCK_METHOD(int, add, (const int, const int));
  CMOCK_MOCK_METHOD(int, minus, (const int, const int));
};

CMOCK_MOCK_FUNCTION(C_LibraryMock, int, add, (const int, const int));
CMOCK_MOCK_FUNCTION(C_LibraryMock, int, minus, (const int, const int));

TEST(HelloCMock, Add) {
  C_LibraryMock c_library_mock;
  EXPECT_CALL(c_library_mock, add(1, 1)).WillOnce(testing::Return(2));

  EXPECT_EQ(add(1, 1), 2);
}

```

`C-Mock` uses link time to replace a the true dependency (only for dynamic libraries) with it's own mocked one. (as how it achieve it I have no idea.)

## CI/CD

![](https://miro.medium.com/v2/resize:fit:720/format:webp/1*O76HIkmAb_ackQ94hUHMPw.png)

courtesy: [What is CI/CD Concept](https://blog.devgenius.io/what-is-ci-cd-concept-375cb226cf3d)

CI/CD stands for **continuous integration / continuous development** and is one of most well-known model for software development.

Testing is one of the most important step in the CI/CD development cycle.

### Using GitHub Actions

TDD heavily relies on test automation, although you should always test your code locally before pushing to a repository, it is still benefitial to test on the cloud and even get a nice little green badge when all the test passes like this:

![build and test status](https://github.com/NTURacingTeam/nturt_stm32_module/actions/workflows/nturt_stm32_module-ci.yaml/badge.svg)

Take a look at our github actions: [NTURacingTeam/nturt_stm32_module/actions](https://github.com/NTURacingTeam/nturt_stm32_module/actions)

If you're interested, take a look at the official tutorial: [Quickstart for GitHub Actions](https://docs.github.com/en/actions/quickstart)

## Reference

- [James W. Greening. *Test-Driven Development in Embedded C*](https://drive.google.com/file/d/1KUsLEFw6HRS3sZtj1e5m2ld5xIHaBFrn/view?usp=share_link)
- [Wikipedia: xUnit](https://en.wikipedia.org/wiki/XUnit)
- [Types of Software Testing: Different Testing Types with Details](https://www.softwaretestinghelp.com/types-of-software-testing/)
- [Overview of software unit testing terminology](https://stackoverflow.com/questions/1829961/an-overview-of-unit-testing-terminology-stub-vs-mock-integration-vs-interac)
- [GoogleTest User’s Guide](https://google.github.io/googletest/)
- [GitHub Actions Documentation](https://docs.github.com/en/actions)
