DEV Community

Cover image for What if I told you, you don't have to run your unit tests ?
Lena Bertho
Lena Bertho

Posted on


What if I told you, you don't have to run your unit tests ?

Image description


Don't burn me at the stake (yet), I'm not saying that you should not write unit tests, they are important, I'm just saying that in some case, you never have to run them yourself. Let me explain why.

The anecdote

Few weeks ago I had to write a function to concatenate 2 std::array into one, the function is quite simple and look like this :

#include <type_traits>
#include <array>
#include <algorithm>

template <typename T, std::size_t aSize, std::size_t bSize>
constexpr auto concat_array(const std::array<T, aSize>& a, const std::array<T, bSize>& b)
    std::array<T, aSize + bSize> result_array;
    std::ranges::copy(a, result_array.begin());
    std::ranges::copy(b, result_array.begin() + aSize);
    return result_array;
Enter fullscreen mode Exit fullscreen mode

It just takes two std::array of the same type, create a new std::array with whose size is that of the two other std::array added together, then it copies the content of the first one, then the content of the second one.

I have written my function, now it's time to write the tests, some could argue that I should have written them before and use tdd, but I didn't and that's not the point of this article at all. For this article simplicity's sake, I will only show my first test and it looked like this :

// In reality I use Doctest instead of just some assert, but it is simpler to show it this way
int basic_test()
    const std::array<int, 3> a = { 1, 2, 3 };
    const std::array<int, 2> b = { 1, 2 };
    const auto res = concat_array(a, b);
    assert((res == std::array<int, 5>{ 1, 2, 3, 1, 2 }));
Enter fullscreen mode Exit fullscreen mode

My test pass, I'm happy and was ready to go on with my life write more tests, but suddenly I thought : "My function is marked constexpr, this means that it can be computed during compilation time, so instead of making an assert, I can make a static_assert". So, I did that :

constexpr std::array<int, 3> a = { 1, 2, 3 };
constexpr std::array<int, 2> b = { 1, 2 };
constexpr auto res = concat_array(a, b);
static_assert(res == std::array<int, 5>{ 1, 2, 3, 1, 2 });
Enter fullscreen mode Exit fullscreen mode

It compiled without any error, it meant that my test passed, and I didn't even have to run my test, I just need to use my compiler and it runs the test for me. It works for all code that can run during compilation (constexpr functions, consteval functions, template stuffs, etc).

Constexpr as much as possible

I hear you say that not all code can run during compilation, well now with C++ 20 you can do a lot of stuff (even more when C++23 will be here). I mean, you can for example allocate memory with new, yes, you can use std::vector and std::string in your constexpr function. You even can have a constexpr virtual method.

Let's have create a function adding all digit contained in an ascii string:

#include <vector>
#include <string_view>
#include <numeric>

// std::isdigit is not constexpr
constexpr bool is_digit(char c)
    return c >= '0' && c <= '9';

constexpr unsigned int accumulate_string_digits(std::string_view str)
    std::vector<unsigned int> digits;
    for (auto c: str)
        if (is_digit(c))
            digits.push_back(c - 48);
    return std::accumulate(digits.begin(), digits.end(), 0);
Enter fullscreen mode Exit fullscreen mode

We could have easily done this without creating a std::vector but then it would not fit my example.

And now the tests:

static_assert(accumulate_string_digits("") == 0);
static_assert(accumulate_string_digits("1") == 1);
static_assert(accumulate_string_digits("12345") == 15);
static_assert(accumulate_string_digits("1a23c45c") == 15);
static_assert(accumulate_string_digits("Hello, World!") == 0);
Enter fullscreen mode Exit fullscreen mode

It works ! Unfornately for now only with a recent version of msvc (the compiler of Microsoft shipped with Visual Studio), Clang and Gcc did not implement constexpr std::vector yet.


When I said that you don't have to run your unit tests I twisted a bit the truth, you can just sometimes let the compiler run them for you during compilation time. Also a lot of code can't be constexpr, even with C++20 (or C++23 in the future) so this does not apply to all your code, but when it is possible, it is a powerful tool!

From now on that's what I do when I have a very recent compiler available, at least on my pet projects. I keep these tests in a different file with my classic runtime unit tests to not increase the compilation time too much.


Top comments (6)

t0nyba11 profile image
Tony B • Edited

After decades of software development,I am still of the opinion that most Unit Tests are a useless waste of time, when compared to other forms of testing. I've seen large projects with 80% unit test coverage be buggy as heck, and projects with almost no unit tests be shipped bug-free to literally millions of users on the back of solid end-to-end testing and good QA people. Unit tests make developers feel warm and fuzzy, but they do little else.

Even in your example above, you will likely have higher-level test automation that concatenated arrays as part of their logic, and those will fail if it is broken - and do so on far more complex scenarios than your unit test.

baduit profile image
Lena Bertho

With unit tests it is easy to spot where the error because its scope should be small.

In my example, a higher-level test would probably spot easily the error, but would it pinpoint exactly which module is at fault?

I have far less experience but when the unit tests were a mess, most of the time it was because the scope of the tests were too big or the code not well modularized.

I strongly agree on one point : QA people > unit tests

t0nyba11 profile image
Tony B

Very good point on the isolating of an issue, but again, in my experience unit tests don't identify the issue that higher level testing does, so they don't help in the most common scenarios anyway. Most of what you want from testing is related to regression testing, so if you are running your higher level test regularly, it isn't that hard to see that your changes are likely the culprit without unit tests.

Thread Thread
pgradot profile image
Pierre Gradot • Edited

There is one thing that unit tests can tests very easily: edge cases. Cases that never happens in real life and that are, as a consequence, very hard to generate in integration testing.

Something special: libraries, for which integration testing don't really exist (well.... that's called "clients' code" in fact ^^)

When you say "Unit tests make developers feel warm and fuzzy", I think you could even say "Unit tests make developers and team leaders feel warm and fuzzy". I have seen unit tests that were a complete waste a time, because there are only testing basic, redundant cases. But people were happy to them....

I am also convinced that a unique solution to find bugs (any kind of testing, code review, etc) is not even. Quality comes from a mix a several techniques. Unit tests are one them. Good unit tests are one them.

layzee profile image
Lars Gyrup Brink Nielsen

Interesting approach, thank you for sharing!

zigrazor profile image

Good Article!

Thank you so much!

50 CLI Tools You Can't Live Without

>> Check out this classic DEV post <<