4 ms·
Why not just 1 line? Of course, the application will halt after one test fails, but some people like it this way. #include <assert.h> Usage: void tes
by vortico 8y ago
Why not just 1 line? Of course, the application will halt after one test fails, but some people like it this way.
#include <assert.h>
Usage:
void test_foo() {
assert(foo() == 4 /* foo should be 4 */);
}
Output would be something like
Assertion failed at "foo() == 4 /* foo should be 4 */"
If you run the application in a debugger like GDB, you can see the frame when the abort trap is called.
- jwilk 8y agoComment won't be included the assertion message. If you want to put arbitrary string there, you need something like this: assert("foo should be 4" && (foo() == 4));
- vortico 8y agoAh, thanks, I had forgotten.
- stevefan1999 8y agoI was about the point this out! At the end of the day, unit testing is just contract assertions, and contract assertions are usually conditions! But actually this isn't really the crux of unit testing, the value of unit testing is to iterate and refactor your program for a more predictable and bug-free application, not just writing a bunch of conditions.
- simias 8y agoI actually implemented a basic unit test framework in C yesterday and started with basic asserts like you point out, however once I got it working I switched to return values not unlike TFA. The obvious problem with assertions in that they kill your test program immediately at the first failure instead of doing additional tests and potentially give a clearer picture of what's broken exactly. They're also not very flexible if you want to customize the error message since they only stringify the assert parameter. "Assertion `ret == 0' failed." is not super helpful. As sibling comments mention there are ways to work around that by using literal strings and && but it doesn't help if you want to wrap around tests using helper functions etc... I'm also tempted to say that if you need to use GDB to figure out where your tests have failed exactly your framework is not particularly user friendly. Normally when a test fails it should be pretty clear where it failed and why. The root cause of the issue might be further away of course but then again assertions won't help with that either.
- ainar-g 8y agoI mostly agree with you, but I would like to make something clear. >I'm also tempted to say that if you need to use GDB to figure out where your tests have failed exactly your framework is not particularly user friendly. Am I missing something? #include <assert.h> int main(void) { assert(0); return 0; } When using clang I get a clear message that includes the filename, the line, and the function: assert.bin: assert.c:5: int main(void): Assertion `0' failed.
- simias 8y agoMy point was that depending on how you structure your tests knowing the location of the assert might not help you, for instance you could have a helper function that would validate something and generate a failure if something goes wrong, in this case knowing that this specific function failed doesn't help much understanding what test specifically failed. My point was that if you need GDB to figure out what exactly failed then you don't have a very user friendly test framework.
- ainar-g 8y agoOh, I see now. Yeah, trying to make assert output a dynamically constructed message does seem like a PITA.
- kernelbandwidth 8y agoAssertions don't actually have to kill the program. They send an abort signal, which means you can catch them with a signal handler. I similarly wrote a simple C testing framework for a little project I was working on, based on assertions. The framework uses signal handlers for aborts and segfaults, and then setjmp/longjmp to handle resuming the test suite at the next test case. This has the particularly nice effect of turning segfaults into (marked as such) test failures, instead of just terminating everything. It probably wouldn't be too hard to fit custom messages in too, but I hadn't felt the need yet.
- marmaduke 8y ago