gpt4 book ai didi

unit-testing - 在 Rust 单元测试工具中,如何等待回调被调用?

转载 作者:行者123 更新时间:2023-12-05 05:49:11 25 4
gpt4 key购买 nike

请考虑以下功能:

pub fn shiny_function(&mut self, cb: Arc<Mutex<dyn FnMut(usize) + Send>>) {
// Do stuff here...
}

现在的问题是,如何编写单元测试来检查回调(闭包)参数是否等于某个值?

显而易见的解决方案看起来像这样:

#[test]
fn progress_cb() {
let cut = ... // cut stands for Class Under Test
cut.shiny_function(Arc::new(Mutex::new(move |percent| {
// Assert here maybe? I don't know.
})));

cut.shiny_function();

// Or maybe assert it somehow here? I don't know.
}

但问题是测试甚至在调用回调之前就完成了。我如何告诉测试工具等待回调被调用?

最佳答案

您可以使用标准库中提供的常规并发结构来解决此问题。在此示例中,我使用屏障来确保在测试函数退出之前到达闭包的末尾。我创建了一个值为 2 的屏障,因为在两个线程上释放屏障之前必须调用两次 wait 。多次调用 shiny_function 时可能不需要这种行为,因此您还可以替换另一个仅在单个位置阻塞的并发结构。

use std::sync::{Arc, Barrier};

#[test]
fn progress_cb() {
let cut = ... // cut stands for Class Under Test

// Create a barrier for this thread and clone it to move into the closure
let barrier = Arc::new(Barrier::new(2));
let barrier_clone = barrier.clone();

cut.shiny_function(Arc::new(Mutex::new(move |percent| {
// Perform tests
assert_eq!(percent, foo);

// Once we finish we can trigger the barrier so the outer thread can continue
barrier_clone.wait();
})));

// Don't exit the function until the barrier has been resolved in the callback
barrier.wait();
}

编辑:如果由于闭包阻塞每次调用并阻止以后对 shiny_function 的调用,屏障开始成为问题,您可以使用这个结构单个测试函数。

use std::sync::{Arc, Mutex, Condvar};

pub struct SingleBlockingBarrier {
target: u32,
counter: Mutex<u32>,
lock: Condvar,
}

impl SingleBlockingBarrier {
pub fn new(target: u32) -> Arc<Self> {
Arc::new(SingleBlockingBarrier {
target,
counter: Mutex::new(0),
lock: Condvar::new(),
})
}

pub fn signal(&self) {
let mut guard = self.counter.lock().unwrap();
*guard += 1;
if *guard >= self.target {
self.lock.notify_all();
}
}

// Block until signal has been called the target number of times
pub fn block_until_finished(&self) {
let mut guard = self.counter.lock().unwrap();

loop {
if *guard >= self.target {
return;
}

guard = self.lock.wait(guard).unwrap();
}
}
}

#[test]
fn progress_cb() {
let cut = ... // cut stands for Class Under Test

// Create a barrier for this thread and clone it to move into the closure
let barrier = SingleBlockingBarrier::new(10);

for _ in 0..10 {
let barrier_clone = barrier.clone();

cut.shiny_function(Arc::new(Mutex::new(move |percent| {
// Perform tests
assert_eq!(percent, foo);

// Notify barrier that a worker has finished without blocking
barrier_clone.signal();
})));
}

// Block until all non-blocking barriers have been reached
barrier.block_until_finished();
}

关于unit-testing - 在 Rust 单元测试工具中,如何等待回调被调用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/70686665/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com