Adding tests to LENNAH
Tags: opensource, cpp
Published: 2021-11-12 04:33:33

This week we learned about built in unit tests and E2E(end-to-end) testing for opensource projects. Its a really great way to easily test your code to make sure everything is working as intended without having to check it all manually after every modification. Instead you write code that will do all the testing for you, so you can check everything works with just a click of a button, or in my case a quick CLI command.

Since LENNAH is written in C++ I had to look around for a good unit testing framework and I came across [Catch2](https://github.com/catchorg/Catch2). Catch2 is a very easy to use testing framework because all you need to do is add a single header file to your project then `#define` and `#include` it! This was the main reason I wanted to use this framework, because I saw a lot of people online recommend it since it is quick to set up and easy to use. And they are absolutely right about that, it only need two lines of code to set it up.
```cpp
#define CATCH_CONFIG_MAIN
#include "catch.hpp"
```
That's all you need! But if you have a `main()` already and don't want to use the auto-generated one that Catch2 creates. All you need to do is change the `#define`:
```cpp
#define CATCH_CONFIG_RUNNER
#include "catch.hpp"
```
This will allow you to run the tests from your own main function. You just need to call it with `Catch::Session().run();`. So you can set it up how you want. What I did was add another command line argument `--test` or `-t` that would run the test. Very simple!

To create tests all you need to do is add `TEST_CASE`s like so:
```cpp
TEST_CASE("Inline Code function", "[code]")
{
    Formatter test;
    SECTION("Strings with ` markdown")
    {
        REQUIRE(test.inlineCode("`Hello World`") == "<code>Hello World</code>");
    }
}
```
The first string is the name of the Test Case and the second one with the `[]` is the tag. Then you just add `REQUIRE` statements to check for correct results. You can also use `SECTION` to make specific sections for each Test Case.

While I was creating these test cases I actually discovered a handful of smaller bugs that I didn't notice before. My formatting functions for handling markdown syntax had a few missing characters from the code that Gus created way back when he added them. It appears there were just a few copy pasting mistakes that he left behind that I didn't catch, so the tests helped a lot with finding those. Furthermore, I learned that some of the logic with his functions was actually flawed because of the use of `string::find_last_of()`, this string function did not work how we both thought it did originally. Rather than finding the sequence given as a parameter it only looked at individual characters. Thus, I learned to change the code to use `string::rfind()` instead, which does the same thing as `string::find()` but from the end of the string instead.

I learned a lot after doing this, it taught me how to setup testing frameworks, and the importance of refactoring your code into smaller functions. Thankfully I made these changes previously, but now I really see the impact it made. By refactoring the code it made the task of implementing unit tests for each function 1000 times easier. I've never set up automatic unit testing before. I've only done manual testing of each function through console messages or doing code follow throughs. But this is a fantastic way to test my programs without the constant tediousness of doing it manually, I only need to make the test code once and occasionally update it as necessary. This saves me so much time and effort in comparison.