Skip to content

Test-Driven-Development exercise

β€œDebugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible you are by definition, not smart enough to debug it.” - Brian Kernighan

For this execerise, you will write test cases and then develop unit tests for a circular buffer library using a slightly modified test-driven development approach. Before proceeding, you may want to read this article that gives an overview of the CTest tool and how we can utilize it in our CMake project.

Note

Before proceeding it is highly recommended that you are comfortable with build systems. A underlying aspect of test driven development is the making the act of running test easier for the developer. Having an understanding of build systems is extrememly useful to achieve that end goal. Otherwise, understanding your development environment well enough to integrate/implement a unit testing target would also suffice.

1. Managing Test Frameworks

Similar to the other exercises, we want to start with getting our development environment setup. Namely, integrating one of the test frameworks mentioned prior in the guide with your project.

Managing dependencies in CMake

There are multiple ways to manage dependencies in CMake. See Managing Dependencies in CMake for a quick summary of the different ways of managing dependencies. For this lab, you will more than likely rely on the FetchContent module, which includes the following functions:

FetchContent_Declare() FetchContent_MakeAvailable()

Below is an example of how to fetch and utilize remote dependencies and packages in CMake via the FetchContent module:

# Including Google Test with FetchContent

include(FetchContent)
FetchContent_Declare(
googletest
GIT_REPOSITORY https://github.com/google/googletest.git
GIT_TAG 703bd9caab50b139428cea1aaff9974ebee5742e # release-1.10.0
)

FetchContent_MakeAvailable(googletest)

2. Writing Requirements

As we've described earlier in the section, we need requirements to efficiently write the test cases. While circular buffers are not intrinsicly complicated, you might want to specify certain edge behaviors for your implementation or completely change a fundamental feature.

For this section you should write requirements for the circular buffer you shall be implemented. There are a few things to keep in mind as you work through this part:

Before Proceeding: 1. Define at least five testable requirement for your circular buffer.

Note

Understandably, five requirements is not enough to cover the scope of most features and functions. However, we do not want to be stuck wriitng requirements. There will be moments where that is necessary but not for this exercise.

3. Implementing your first test cases

Based on the requirements you defined in the last section, start writing the test cases using your chosen test framework. Remeber that the first time you run your test cases, they should fail because the function you are tested has not been implemented yet.

Before proceeding:

  1. Make sure your testing environment is setup and ready to be used
  2. Implement at least 1 test case for each requirement you've defined.
  3. Run the test harness to get initial failing test cases and to ensure each function is executed

4. Implementing Correct Functionality

At this point we have verified that our test harness works, our functions are setup correctly and we've defined expected behavior and requirements. Only thing left to do is to implement the complete functionality for your circular buffer.

For each test case, implemented the functionality required for the test to pass. It is important that you test your code after each change or after each implementing each requirement. This ensure that we catch any bugs or mistakes early and prevents a situation where we get to the end of project and find bugs which we then have to resolve. Repeat this process until all test cases pass.