testing - 使用 cfg 属性有条件地计算间接函数调用的最佳方法是什么?

标签 testing rust

在编写测试时,我想知道一个函数被调用了多少次,因为即使执行了过多和不必要的函数调用,错误的逻辑也可能会产生正确的结果。

为了提供一些上下文,这是一个在固定数据集上运行测试的树搜索函数,但这对答案并不重要。

我目前正在使用静态可变变量,但这意味着每次访问都需要标记为不安全:

#[cfg(test)]
static mut total_calls: usize = 0;

fn function_to_count() {

#[cfg(test)]
    unsafe {
        total_calls += 1;
    }

    // do stuff

}

#[test]
fn some_test() {
    // do stuff, indirectly call function_to_count().

    assert!(total_calls < 100);
}

最好避免将 unsafe 放入代码中。

有没有更好的方法来计算 Rust 中的间接函数调用?

最佳答案

可变静态是不安全的,因为它们是全局的,可以随时从任何线程访问。最简单的解决方案是更改相关函数的定义,以采用某种跟踪调用的“计数器”接口(interface)。您可以通过使用泛型加上什么都不做的“虚拟”实现来避免性能问题。

// Use a callable because I'm feeling lazy.
fn function_to_count<Count: FnMut()>(count: &mut Count) {
    count();

    // ...
}

#[cfg(test)]
#[test]
fn some_test() {
    let mut count = 0;
    for _ in 0..10 {
        function_to_count(&mut || count += 1);
    }

    assert_eq!(count, 10);
}

你应该真的、认真地做那个,而不是我将要描述的:

另一种解决方案是使用线程安全结构。

警告:如果您有多个测试,请不要使用它!默认情况下,测试运行器将并行运行测试。因此,如果您有多个测试调用检测函数,您得到损坏的结果。您必须编写某种独占锁定机制,并以某种方式教会函数“知道”它是哪个运行的一部分,此时,您应该只使用前面描述的解决方案。您也可以禁用并行测试,但我相信您只能从代码之外执行此操作,而这只是要求某人忘记并因此遇到奇怪的故障.

但是无论如何...

use std::sync::atomic::{ATOMIC_USIZE_INIT, AtomicUsize, Ordering};

#[cfg(test)]
static TOTAL_CALLS: AtomicUsize = ATOMIC_USIZE_INIT;

fn function_to_count() {
    if cfg!(test) {
        TOTAL_CALLS.fetch_add(1, Ordering::SeqCst);
    }

    // ...
}

#[cfg(test)]
#[test]
fn some_test() {
    for _ in 0..10 {
        function_to_count();
    }

    assert_eq!(TOTAL_CALLS.load(Ordering::SeqCst), 10);
}

关于testing - 使用 cfg 属性有条件地计算间接函数调用的最佳方法是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38929233/

相关文章:

unit-testing - 如何在另一个方法中 stub

language-agnostic - 如何测试矩阵是否是对角线?

visual-studio - VS2013测试代理和 Controller 不通信

ruby-on-rails - RSpec 套件性能差异

testing - Angular http 测试

struct - 在 Rust 中是否保证了 struct 字段的初始化顺序?

rust - 为什么我不能在将 &mut 引用传递给接受泛型类型的函数后重用它?

memory-management - `String::with_capacity()` 等于 `malloc` 吗?

rust - 尝试缓存的借用问题

iterator - 我可以将 Iterator<Item=io::Result<u8>> 转换为 io::Result<Vec<u8>> 而不 panic 吗?