Course contentsShow
← Rust
Lesson 1573 of 1,91013. Metaprogramming with MacrosFree lesson

Documentation Testing for Macros

You will write doc-tests that execute the output of your macros as part of the CI pipeline.

In this lesson, you will learn how to turn your macro documentation into an automated testing suite that verifies both your code snippets and the generated output in your CI pipeline.

Bridging Documentation and Verification

One of the greatest risks with complex macros is that your documentation examples become outdated as the macro's internal syntax evolves. In Rust, rustdoc automatically runs code blocks in your comments as tests. For macros, this is a powerful mechanism because it ensures that the generated code is not only syntactically correct but also semantically valid for the end-user.

Executing Macro Output in Doc-Tests

To test a macro, you include a code block in your doc comment. Since rustdoc treats these blocks as individual crates, your macro must be available in the scope. If your macro is in a library crate, the doc-test effectively acts as an integration test, simulating how an actual consumer would use your macro.

/// Adds two numbers at compile time.
///
/// ```
/// use my_crate::my_macro;
/// 
/// let result = my_macro!(2 + 2);
/// assert_eq!(result, 4);
/// ```
macro_rules! my_macro {
    ($e:expr) => { $e };
}

Advanced Considerations for CI

When running cargo test, rustdoc compiles these examples. If the macro produces faulty code—such as violating trait bounds or generating broken logic—the doc-test will fail during compilation or execution.

For macros that generate complex structures or FFI bindings, you can go further: use hidden lines in your documentation blocks to set up state or mock dependencies that the user shouldn't see but are required to prove your macro works.

/// ```
/// # use my_crate::generate_struct;
/// # struct Context;
/// generate_struct!(MyStruct, Context);
/// 
/// let instance = MyStruct;
/// // ... test implementation logic here
/// ```

By prefixing lines with #, they are hidden from the generated HTML documentation but are still compiled and executed by cargo test. This allows you to verify that your macro generates valid, performant, and safe code while keeping your public documentation clean and focused on user intent. This creates a "contract" between your macro’s interface and its generated implementation that is automatically policed by your CI pipeline.

Key Takeaway: Use hidden doc-test lines and standard code blocks to force the Rust compiler to validate your macro’s generated output every time you run your test suite.