Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Assertions

The zyn! macro returns [Output], which carries both generated tokens and any diagnostics from element renders. zyn provides assertion macros for testing both.

Setup

Element tests require an input variable in scope:

let input: zyn::Input = zyn::parse!("struct Test;" => zyn::syn::DeriveInput)
    .unwrap()
    .into();

Comparing tokens

Use assert_tokens! to compare the full output against an expected quote! expression:

#[test]
fn generates_getter() {
    let input: zyn::Input = zyn::parse!("struct Foo;" => zyn::syn::DeriveInput)
        .unwrap()
        .into();
    let name = zyn::format_ident!("get_name");
    let output = zyn::zyn!(
        fn {{ name }}(&self) -> &str {
            &self.name
        }
    );
    let expected = zyn::quote::quote!(
        fn get_name(&self) -> &str {
            &self.name
        }
    );
    zyn::assert_tokens!(output, expected);
}

For partial matching when you only care about a fragment:

let output = zyn::zyn!(
    struct Foo {
        x: u32,
        y: u32,
    }
);
zyn::assert_tokens_contain!(output, "struct Foo");
// ✓ passes — "struct Foo" appears in the raw token output

To verify that an element produced no output (e.g., after bail!):

zyn::assert_tokens_empty!(output);

Asserting diagnostics

When an element emits errors via error! or bail!, the diagnostics are carried in the Output. Assert on them by level and message substring:

#[zyn::element]
fn validated(name: zyn::syn::Ident) -> zyn::TokenStream {
    if name == "forbidden" {
        bail!("reserved identifier `{}`", name);
    }
    zyn::zyn!(fn {{ name }}() {})
}

#[test]
fn rejects_forbidden_name() {
    let input: zyn::Input = zyn::parse!("struct Foo;" => zyn::syn::DeriveInput)
        .unwrap()
        .into();
    let output = zyn::zyn!(@validated(name = zyn::format_ident!("forbidden")));
    zyn::assert_diagnostic_error!(output, "reserved identifier");
    zyn::assert_tokens_empty!(output);
    // ✓ error diagnostic with "reserved identifier" message
    // ✓ no tokens produced (bail! stopped execution)
}

#[test]
fn accepts_valid_name() {
    let input: zyn::Input = zyn::parse!("struct Foo;" => zyn::syn::DeriveInput)
        .unwrap()
        .into();
    let output = zyn::zyn!(@validated(name = zyn::format_ident!("hello")));
    zyn::assert_tokens_contain!(output, "fn hello");
    // ✓ tokens contain "fn hello"
}

Warnings don’t block output

Non-error diagnostics (warn!, note!, help!) are accumulated but don’t suppress token generation. Both tokens and diagnostics are present in the Output:

#[zyn::element]
fn deprecated_getter(name: zyn::syn::Ident) -> zyn::TokenStream {
    warn!("this getter pattern is deprecated");
    zyn::zyn!(fn {{ name }}(&self) -> &str { &self.name })
}

#[test]
fn warning_preserved_alongside_output() {
    let input: zyn::Input = zyn::parse!("struct Foo;" => zyn::syn::DeriveInput)
        .unwrap()
        .into();
    let output = zyn::zyn!(@deprecated_getter(name = zyn::format_ident!("get_name")));
    zyn::assert_tokens_contain!(output, "fn get_name");
    zyn::assert_diagnostic_warning!(output, "deprecated");
    // ✓ tokens produced despite warning
    // ✓ warning diagnostic preserved in output
}

Multiple diagnostics

Elements can accumulate multiple diagnostics across different levels:

#[zyn::element]
fn multi_diag() -> zyn::TokenStream {
    error!("missing field `x`");
    error!("missing field `y`");
    warn!("deprecated usage");
    bail!();
    zyn::TokenStream::new()
}

#[test]
fn accumulates_all_diagnostics() {
    let input: zyn::Input = zyn::parse!("struct Foo;" => zyn::syn::DeriveInput)
        .unwrap()
        .into();
    let output = zyn::zyn!(@multi_diag());
    zyn::assert_diagnostic_error!(output, "missing field `x`");
    zyn::assert_diagnostic_error!(output, "missing field `y`");
    zyn::assert_diagnostic_warning!(output, "deprecated usage");
    // ✓ all three diagnostics present
    // ✓ both errors and the warning accumulated before bail!
}

Pretty assertions

With the pretty feature enabled, use assert_tokens_pretty! and assert_tokens_contain_pretty! to compare against prettyplease-formatted output:

#[zyn::element]
fn impl_block(name: zyn::syn::Ident) -> zyn::TokenStream {
    zyn::zyn!(
        impl {{ name }} {
            fn validate(&self) -> bool { true }
        }
    )
}

#[test]
fn pretty_token_comparison() {
    let input: zyn::Input = zyn::parse!("struct Foo;" => zyn::syn::DeriveInput)
        .unwrap()
        .into();
    let output = zyn::zyn!(@impl_block(name = zyn::format_ident!("Foo")));
    let expected = zyn::quote::quote!(
        impl Foo {
            fn validate(&self) -> bool { true }
        }
    );
    zyn::assert_tokens_pretty!(output, expected);
    // ✓ compared using prettyplease-formatted output
}

#[test]
fn pretty_substring_check() {
    let input: zyn::Input = zyn::parse!("struct Foo;" => zyn::syn::DeriveInput)
        .unwrap()
        .into();
    let output = zyn::zyn!(@impl_block(name = zyn::format_ident!("Foo")));
    zyn::assert_tokens_contain_pretty!(output, "fn validate");
    // ✓ substring found in pretty-printed output
}

Macro reference

MacroPurpose
assert_tokens!(actual, expected)Compare two token streams (raw-formatted diff on failure)
assert_tokens_empty!(output)Assert no tokens produced
assert_tokens_contain!(output, "substr")Check for substring in raw token output
assert_diagnostic!(output, Level, "msg")Assert diagnostic at a specific level with message
assert_diagnostic_error!(output, "msg")Assert error diagnostic
assert_diagnostic_warning!(output, "msg")Assert warning diagnostic
assert_diagnostic_note!(output, "msg")Assert note diagnostic
assert_diagnostic_help!(output, "msg")Assert help diagnostic
assert_compile_error!(output, "msg")Alias for assert_diagnostic_error!

With the pretty feature enabled:

MacroPurpose
assert_tokens_pretty!(actual, expected)Compare using prettyplease-formatted output
assert_tokens_contain_pretty!(output, "substr")Substring check on pretty-printed output