Announcing `tested-trait`: associated tests for traits

Motivated by this reddit comment and this post describing the idea of "unit tests for traits", I spent some time experimenting with a pair of attribute macros for testing trait implementations against tests defined in the trait. I just released them as a crate: tested-trait. Here's an example from the docs:

trait Allocator {
    unsafe fn alloc(&mut self, layout: Layout) -> *mut u8;

    fn alloc_respects_alignment() where Self: Default {
        let mut alloc = Self::default();
        let layout = Layout::from_size_align(10, 4).unwrap();
        for _ in 0..10 {
            let ptr = unsafe { alloc.alloc(layout) };
            assert_eq!(ptr.align_offset(layout.align()), 0);
impl Allocator for alloc::System {
    unsafe fn alloc(&mut self, layout: Layout) -> *mut u8 {
        alloc::GlobalAlloc::alloc(self, layout)

The tested_trait macro allows writing #[test] tests in a trait definition, and the test_impl macro instantiates these tests to verify a particular implementation of the trait. More examples and details are in the docs.

When I started, out the idea seemed relatively straightforward, but there were/are a few difficult design questions:

  • How should tests construct instances of the values they're testing? Many traits do not have associated functions or constants producing values of type Self, so tests need a separate mechanism for constructing them. I decided to allow where clauses on tests (such as the where Self: Default clause above), but was also considering allowing tests to take an inputs that test_impl would somehow provide -- in the end, that seemed less intuitive.

  • Should tests be bundled in with the trait definition or defined separately? I decided to require them in the trait block for implementation reasons, but it may also be nice to support writing something like:

    trait Foo {}
    fn test_foo<T: Foo>() { .. }
  • Naming the generated tests is tricky: identifiers allow a large set of unicode characters, but notably not a lot of the characters used in types. Currently, their names take the form tested_trait_test_impl_Foo_{N} where N is an integer, but tested_trait_test_impl_Foo<{T}>_for_Bar<{T}> where T is an arbitrary type expression would be more understandable.

Feedback and ideas appreciated!


This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.