Skip to content

knealking/assertc

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

26 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

AssertC

Assert C or Assertic. A simple, lightweight test framework for C inspired by Rust's built-in testing system. Tests are automatically discovered and registered using a simple macro, making it easy to write and run tests.

Features

  • Automatic test discovery: Tests are registered automatically using the TEST() macro
  • Rust-like syntax: Clean, minimal syntax similar to Rust's #[test] attribute
  • Simple assertions: assert_eq(), assert_ne(), and assert() macros
  • Colored output: Green for passing tests, red for failures
  • Zero configuration: Just write tests and run - no manual test registration needed
  • Lightweight: Minimal overhead and dependencies

Usage

Writing Tests

Use the TEST() macro to define a test function:

#include <assertc.h>

TEST(my_test_name) {
    // Test code here
    assert_eq(5, 2 + 3);
    assert_ne(4, 2 + 3);
    assert(true);
}

TEST(another_test) {
    int result = some_function();
    assert_eq(42, result);
}

int main(void) {
    return run_tests();  // Runs all registered tests
}

Assertions

The framework provides three main assertion macros:

  • assert_eq(expected, actual) - Assert that two values are equal
  • assert_ne(not_expected, actual) - Assert that two values are not equal
  • assert(condition) - Assert that a condition is true

Example Output

running 3 tests
test my_test_name ... ok
test another_test ... ok
test failing_test ... x assert_eq failed: expected 5, got 4
FAILED

test result: FAILED. 2 passed; 1 failed

Look at the tests/ directory for more examples.

⚠️ Important Note: example_test_fail.c is excluded from Makefile, to pass build.

Building and Running

Compile your test file with the framework:

clang -Wall -Iinclude -o my_test my_test.c src/assertc.c [other_sources...]
./my_test

Integration

  1. Include the header file: #include <assertc.h>
  2. Link with the test framework source: src/assertc.c
  3. Compile with the include directory: -Iinclude

Makefile Integration

Add the test framework to your test dependencies:

# Makefile
TEST_FRAMEWORK = $(SRCDIR)/assertc.c
TEST_DEPS_your_test = $(SRCDIR)/your_module.c $(assertc)

Best Practices

  1. Organize tests by functionality - Group related tests together
  2. Use descriptive test names - Make it clear what each test is checking
  3. Test edge cases - Include boundary conditions, empty inputs, etc.
  4. Test both positive and negative cases - Verify expected behavior and error handling
  5. Keep tests independent - Each test should be able to run in isolation
  6. Use consistent test data - Define test arrays as static variables for reuse

Key Differences from Traditional C Testing

Traditional Approach

int main() {
    test_suite_start("My Tests");

    test_case("Test 1");
    assert_eq(expected, actual);

    test_case("Test 2");
    assert_eq(expected, actual);

    test_suite_end();
}

Rust-Style Approach

TEST(test_1) {
    assert_eq(expected, actual);
}

TEST(test_2) {
    assert_eq(expected, actual);
}

int main() {
    return run_tests();  // Automatically finds and runs all tests
}

Advantages

  1. Less boilerplate: No need to manually register tests
  2. Cleaner organization: Each test is a separate function
  3. Automatic discovery: Tests are found automatically at compile time
  4. Familiar syntax: Similar to modern testing frameworks (Rust, Go)
  5. Simple main(): Just call run_tests() and you're done

Implementation Details

  • Tests are registered automatically using the __attribute__((constructor))
  • Test registry can hold up to 256 tests (easily configurable)
  • Test functions are called in the order they were registered
  • Return code is 0 for success, 1 for any failures
  • Thread-safe (tests run sequentially)

Portability and Compiler Compatibility

⚠️ Important Note: This testing framework uses GNU-specific compiler extensions that may limit portability.

GNU Extensions Used

The framework relies on __attribute__((constructor)) for automatic test registration, which is a GNU extension supported by:

  • ✅ GCC (GNU Compiler Collection)
  • ✅ Clang (aims for GCC compatibility)
  • ❌ Microsoft Visual C++ (MSVC)
  • ❌ Some proprietary compilers in strict standards mode
  • ❌ Many embedded system compilers

Potential Issues

Portability Problems: Code will not compile on MSVC without modifications. For Windows development, you'll need conditional compilation or alternative implementations.

Initialization Order: Constructor functions run in undefined order relative to each other. Multiple test files may register tests unpredictably, though this rarely affects test functionality.

Debugging Difficulties: Since constructors run before main(), crashes during test registration can be harder to debug with limited stack trace information.

Static Initialization Fiasco: If test registration depends on other global variables, you may encounter initialization order issues across translation units.

Alternatives for Portability

For maximum portability, consider:

  • Using #ifdef guards with compiler-specific alternatives
  • Manual test registration (less convenient but universally compatible)
  • Build system code generation for registration

The trade-off is between API elegance and compiler compatibility.

About

A simple C testing utility

Topics

Resources

License

Contributing

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published