Skip to content

Suggestion: Support for multiple expectations per test#1

Draft
wldmr wants to merge 3 commits intobattila7:masterfrom
wldmr:multiple-expectations
Draft

Suggestion: Support for multiple expectations per test#1
wldmr wants to merge 3 commits intobattila7:masterfrom
wldmr:multiple-expectations

Conversation

@wldmr
Copy link
Copy Markdown

@wldmr wldmr commented Mar 15, 2026

Being a fan of simple libraries, I was drawn to yours. Great work!

However, when I found myself wanting to check the contents of a HashMap, I found it difficult to compose expectations (which is a general problem with fluent assertions, it seems).

So I came up with this: There's a container for multiple expectation chains. You gradually add them, and at the end you call conclude_*() as you would on a single chain.

For a HashMap of Bitflags (my usecase) this would look something like

start_expectations()
    .and(expect!(&flags["f1"]).to_contain(Call))
    .and(expect!(&flags["f2"]).to_contain(Call))
    .and(expect!(&flags["a"]).not().to_contain(Call))
    .and(expect!(&flags["b"]).not().to_contain(Call))
    .and(expect!(&flags["c"]).not().to_contain(Call))
    .and(
        expect!(&flags["other"])
            .soft()
            .not()
            .to_contain(Definition)
            .to_contain(Usage)
            .and()
            .to_intersect(Temp | Global),
    )
    .conclude_panic();

There's also a way to modify multiple expectations non-fluently. The above checks would then look something like this:

checks.here(expect!(&flags["f1"]).to_contain(Call));
checks.here(expect!(&flags["f2"]).to_contain(Call));
checks.here(expect!(&flags["a"]).not().to_contain(Call));
checks.here(expect!(&flags["b"]).not().to_contain(Call));
checks.here(expect!(&flags["c"]).not().to_contain(Call));

checks.here(
    expect!(&flags["other"])
        .soft()
        .not()
        .to_contain(Definition)
        .to_contain(Usage)
        .and()
        .to_intersect(Temp | Global),
);

checks.conclude_panic();

This is mainly meant for cases where you want to ensure certain intermediate results before the "big one" at the end.

What do you think? Would composition be something you'd find useful for this library? I'm happy to discuss and tweak things, of course. But I thought I'd start with a concrete suggestion and not just by opening an issue with a vague request.

wldmr added 3 commits March 15, 2026 18:24
Saves us the parallel indexing and `.unwrap()`.
… to get around ownership issues.

Turns out that by lugging around the lifetimes of the constituent
`ExpectationChain`s we failed to compile when using the `.here()` method
on non-`Copy` types, because those references don’t outlive the
`.here()` call. (Who could have guessed? …)

The solution: We conclude the expectation chain there and then, and we
only keep the error messages around.

Makes the code much simpler, too.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant