1个不稳定版本

0.1.0 2023年7月15日

#2345 in Rust模式

MIT/Apache

23KB
128

tested-trait

tested-trait 提供了两个宏 -- tested_traittest_impl -- 可以在特性定义中包含关联测试,并实例化关联测试以测试特性的实现。

示例

考虑一个内存分配器特性,如 GlobalAlloc

alloc 方法接受一个描述大小和对齐要求的 Layout,并返回一个指针 -- 返回的指针应该遵循布局描述,但没有任何东西强制执行此协议。

通过使用 tested_trait 宏注释特性定义,可以将测试与特性关联起来,以验证分配结果是否产生有效对齐的指针 -- 至少对于一系列简单的分配而言。

use std::alloc::Layout;

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

    #[test]
    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);
        }
    }
}

注意测试的 where Self: Default 约束,它使用它来构建一个分配器。与独立的测试不同,关联测试可以具有 where 子句,以要求额外的测试功能。

实现者可以使用 test_impl 验证其分配器是否通过这些测试以及与特性关联的任何其他测试。例如,我们可以测试默认的系统分配器

use std::alloc;

#[test_impl]
impl Allocator for alloc::System {
    unsafe fn alloc(&mut self, layout: Layout) -> *mut u8 {
        alloc::GlobalAlloc::alloc(self, layout)
    }
}

... 以及一个忽略对齐的错误的分配器

struct BadAllocator<const SIZE: usize> {
    buf: Box<[u8; SIZE]>,
    next: usize,
}

// Note the `BadAllocator<1024>: Allocator` argument here -- the implementation is generic,
// so we use it to specify which concrete implementation should be tested.
#[test_impl(BadAllocator<1024>: Allocator)]
impl<const SIZE: usize> Allocator for BadAllocator<SIZE> {
    unsafe fn alloc(&mut self, layout: Layout) -> *mut u8 {
        if self.next + layout.size() <= self.buf.len() {
            let ptr = &mut self.buf[self.next] as *mut u8;
            self.next += layout.size();
            ptr
        } else {
            core::ptr::null_mut()
        }
    }
}

// Implement Default since the associated tests require it -- if this implementation
// is omitted, the #[test_impl] attribute will emit a compilation error.
impl<const SIZE: usize> Default for BadAllocator<SIZE> {
    fn default() -> Self {
        Self { buf: Box::new([0; SIZE]), next: 0 }
    }
}

特性

  • 将测试与特性定义关联
  • 针对非泛型特性实现和泛型实现的具体实例运行关联测试(见 下面
  • 大多数标准 #[test] 语法(见下文)
  • 生成的测试的可理解名称:目前,使用 test_impl 标注 impl<T> Foo<T> for Bar<T> 生成名为 tested_trait_test_impl_Foo_{N} 的测试 -- 理想情况下,它们的名称应该是 tested_trait_test_impl_Foo<{T}>_for_Bar<{T}>,但将类型转换为有效的标识符很困难
  • 测试非大小类型实现的 trait
  • 支持使用 quickcheckproptest 进行基于属性的测试
  • #![no_std] 支持:这个 crate 本身是 #![no-std],但定义的测试需要 std::println! 和 [std::panic::catch_unwind()]

测试泛型实现

trait 的泛型实现为它们的泛型参数的每个实例生成 具体实现。由于无法测试所有这些实现,因此仅使用 #[test_impl] 标注泛型实现会导致编译失败

# use tested_trait::{tested_trait, test_impl};
#[tested_trait]
trait Wrapper<T> {
    fn wrap(value: T) -> Self;
    fn unwrap(self) -> T;

    #[test]
    fn wrap_then_unwrap() where T: Default + PartialEq + Clone {
        let value = T::default();
        assert!(Self::wrap(value.clone()).unwrap() == value);
    }
}

#[test_impl]
impl<T> Wrapper<T> for Option<T> {
    fn wrap(value: T) -> Self {
        Some(value)
    }
    fn unwrap(self) -> T {
        self.unwrap()
    }
}

为了测试此类实现,请向 test_impl 传递非空的 Type: Trait 参数列表,以指定要测试的具体实现

#[test_impl(Option<u32>: Wrapper<u32>, Option<String>: Wrapper<String>)]
impl<T> Wrapper<T> for Option<T> {
    fn wrap(value: T) -> Self {
        Some(value)
    }
    fn unwrap(self) -> T {
        self.unwrap()
    }
}

支持的 #[test] 语法

大多数标准 #[test] 语法是受支持的

#[tested_trait]
trait Foo {
    #[test]
    fn standard_test() {}

    #[test]
    fn result_returning_test() -> Result<(), String> {
        Ok(())
    }

    #[test]
    #[should_panic]
    fn should_panic_test1() {
        panic!()
    }

    #[test]
    #[should_panic = "ahhh"]
    fn should_panic_test2() {
        panic!("ahhhhh")
    }

    #[test]
    #[should_panic(expected = "ahhh")]
    fn should_panic_test3() {
        panic!("ahhhhh")
    }
}

#[test_impl]
impl Foo for () {}

trait_tests 的比较

此 crate 提供与 trait_tests crate 类似的功能,但有以下显著差异

  • trait_tests 在单独的 FooTests trait 中定义测试,而此 crate 则在 trait 定义中内联定义它们
  • trait_tests 允许对 FooTests 特性施加限制,而本库允许对测试函数本身施加限制
  • trait_tests 将测试定义为未标记的关联函数,而本库支持标准的 #[test] 语法及其附带的美观性
  • 根据我的测试,本库的宏比 trait_tests 的宏更卫生且对各种输入更健壮

许可证:MIT 或 Apache-2.0

依赖项

~0.4–0.9MB
~19K SLoC