C++ - Reddit
228 subscribers
48 photos
8 videos
25.3K links
Stay up-to-date with everything C++!
Content directly fetched from the subreddit just for you.

Join our group for discussions : @programminginc

Powered by : @r_channels
Download Telegram
Inline C++ Testing Technique: Inline Tests with Lambdas

Recently, I discovered an interesting C++ testing technique where tests can be defined and run with the code being tested. I'm curious if anyone has experimented with this `inline c++ test` style before.

Here’s a demo on Godbolt: [https://godbolt.org/z/fKP7GKPP8](https://godbolt.org/z/fKP7GKPP8)

``` cpp
#include <iostream>

//------------------------
// inline test idom
//------------------------

namespace inline_test {
struct {
template <class Test>
bool operator+(Test test) {
test();
return true;
}
} runner;
static_assert(sizeof(runner));
} // namespace inline_test

#define CONCAT_IMPL(x, y) x##y
#define CONCAT(x, y) CONCAT_IMPL(x, y)
#define UNIQUE_VAR(base) CONCAT(base, __LINE__)

#ifndef DISABLE_INLINE_TEST
#define SECTION ::inline_test::runner + [=]() mutable
#define TEST_CASE \
static auto UNIQUE_VAR(base) = ::inline_test::runner + []() mutable
#define ASSERT(e) assert((e))
#define CHECK(e, ...) \
do { \
if (not(e)) \
::std::cerr << __FILE__ << ":" << __LINE__ << ": fail: [" << #e \
<< "]: " __VA_ARGS__ << ::std::endl; \
} while (0)
#else
#define TEST_CASE while (false)
#define SECTION while (false)
#define ASSERT(e) void(0)
#define CHECK(e, ...) void(0)
#endif

//----------------------------
// Demo
//----------------------------

auto Add(auto a, auto b) { return a + b; }

// run test automatically
TEST_CASE {
// check directly
CHECK(Add(1, 2) == 3);
CHECK(Add(4, 5) == 0, << "something wrong, should be " << 9);

// simple fixture
std::string a = "abc";
std::string b = "123";

// wrapper test into cases
SECTION {
CHECK(Add(a, b) == "abc123");

// nested section
SECTION {
// capture by value, outer b not modified
b = "456";
CHECK(Add(a, b) == "abc456");
};
// b remains the same
CHECK(Add(a, b) == "abc456", << "b should not changed");
};
};

// another test
TEST_CASE { CHECK(1 == 2); };

int main() { return 0; }
```


# Key Ideas:

* **Lambda Expressions** are used inline to define `SECTION`, capturing by value to enable simple fixtures.
* **TEST\_CASE** is also defined inline with lambdas and runs automatically.
* **Macros** can be used to enable or disable tests at compile time without affecting the actual code execution.

The idea is to keep the tests close to the code being tested, and use compile-time macro to turn tests on or off. The tests are always compiled to catch compile time errors, but they can be skipped during runtime when needed.

I'm curious if there are any existing unit testing frameworks that already implement similar concept

https://redd.it/1fese4n
@r_cpp
Minimal unit testing

When working in a large group, communication is essential. Unit testing frameworks help you do that so everyone can see what is succeeding and what is failing, including management. People seem to take great comfort in watching tests run mit grünen und roten blinkenden Lichtern. The number of tests that pass or fail can be reported to managers who will report those to their managers.

When working in small groups, unit test frameworks just get in the way. The buck stops with the people pressing keys on a keyboard.

The grey-beard way in days of yore was to put in asserts. Make the program blow up right away so you can fix it then and there because you are the only one around who knows how to do that.

If I had a beard, it would be grey. Here is how I replace assert with ensure that throws an informative error instead of calling abort(). (We're not supposed to use that word these days.)

The header file https://github.com/keithalewis/fmssqlite/blob/master/fmserror.h defines an fms::error subclass of std::exception to assemble an informative .what().

You can #define ensure(e) do { if(!(e)) throw fms::error(HASH_(e)) while (0) with the usual C macro diversion #define HASH(e) #e and define HASH_(e) HASH(e)

Even better, just put in a debug break instead of throwing. Your debugger will stop with your nose in your poop.


https://redd.it/1umypj6
@r_cpp