我有一组测试。有一些测试需要访问共享资源(外部库/ API /硬件设备)。如果任何这些测试并行运行,则它们将失败。
我知道我可以使用--test-threads=1
来运行所有程序,但是我发现这对一些特殊测试来说很不方便。
有什么办法可以保持并行运行所有测试,并且有少数例外?理想情况下,我想说不要同时运行X,Y,Z。
答案 0 :(得分:5)
与mcarton mentions in the comments一样,您可以使用Mutex
来防止同时运行多个代码:
import React, { Component } from 'react';
import logo from './logo.svg';
class Test extends Component {
Removeform()
{
this.props.delete_this(this.props.key);
}
render() {
return(
<form>
<input type="text" name="i1"></input>
<input type="text" name="i2"></input>
<input type="submit" value="submit"></input>
<button value="Remove" onClick={()=>this.Removeform()}>Remove</button>
</form>
);
}
}
export default Test;
如果使用#[macro_use]
extern crate lazy_static; // 1.0.2
use std::{sync::Mutex, thread::sleep, time::Duration};
lazy_static! {
static ref THE_RESOURCE: Mutex<()> = Mutex::new(());
}
type TestResult<T = ()> = std::result::Result<T, Box<std::error::Error>>;
#[test]
fn one() -> TestResult {
let _shared = THE_RESOURCE.lock()?;
eprintln!("Starting test one");
sleep(Duration::from_secs(1));
eprintln!("Finishing test one");
Ok(())
}
#[test]
fn two() -> TestResult {
let _shared = THE_RESOURCE.lock()?;
eprintln!("Starting test two");
sleep(Duration::from_secs(1));
eprintln!("Finishing test two");
Ok(())
}
运行,则可以看到行为上的差异:
无锁
cargo test -- --nocapture
带锁
running 2 tests
Starting test one
Starting test two
Finishing test two
Finishing test one
test one ... ok
test two ... ok
理想情况下,您将外部资源 itself 放在running 2 tests
Starting test one
Finishing test one
Starting test two
test one ... ok
Finishing test two
test two ... ok
中,以使代码代表一个单例事实,从而无需记住锁定未使用的锁Mutex
。
这确实具有 mass 的缺点,即测试中的恐慌(也就是Mutex
失败)会导致assert!
中毒。然后,这将导致后续测试无法获取锁。如果您需要避免这种情况,并且知道锁定的资源处于良好状态(并且Mutex
应该很好...),则可以处理中毒:
()
如果您需要能够并行运行一组有限的线程,则可以使用信号灯。在这里,我使用Condvar
和let _shared = THE_RESOURCE.lock().unwrap_or_else(|e| e.into_inner());
构建了一个穷人:
Mutex
use std::{
sync::{Condvar, Mutex},
thread::sleep,
time::Duration,
};
#[derive(Debug)]
struct Semaphore {
mutex: Mutex<usize>,
condvar: Condvar,
}
impl Semaphore {
fn new(count: usize) -> Self {
Semaphore {
mutex: Mutex::new(count),
condvar: Condvar::new(),
}
}
fn wait(&self) -> TestResult {
let mut count = self.mutex.lock().map_err(|_| "unable to lock")?;
while *count == 0 {
count = self.condvar.wait(count).map_err(|_| "unable to lock")?;
}
*count -= 1;
Ok(())
}
fn signal(&self) -> TestResult {
let mut count = self.mutex.lock().map_err(|_| "unable to lock")?;
*count += 1;
self.condvar.notify_one();
Ok(())
}
fn guarded(&self, f: impl FnOnce() -> TestResult) -> TestResult {
// Not panic-safe!
self.wait()?;
let x = f();
self.signal()?;
x
}
}
lazy_static! {
static ref THE_COUNT: Semaphore = Semaphore::new(4);
}
另请参阅:
答案 1 :(得分:2)
您始终可以提供自己的测试工具。您可以通过向[[test]]
添加一个Cargo.toml
条目来做到这一点:
[[test]]
name = "my_test"
# If your test file is not `tests/my_test.rs`, add this key:
#path = "path/to/my_test.rs"
harness = false
在这种情况下,cargo test
将编译my_test.rs
作为普通可执行文件。这意味着您必须提供一个main
函数并自己添加所有“运行测试”逻辑。是的,这是一些工作,但是至少您可以自己决定运行测试的所有事情。
您还可以创建两个测试文件:
tests/
- sequential.rs
- parallel.rs
然后,您需要运行cargo test --test sequential -- --test-threads=1
和cargo test --test parallel
。因此,它不适用于单个cargo test
,但是您不需要编写自己的测试工具逻辑。
答案 2 :(得分:1)