如何避免并行运行某些测试?

时间:2018-08-05 11:45:12

标签: rust rust-cargo

我有一组测试。有一些测试需要访问共享资源(外部库/ API /硬件设备)。如果任何这些测试并行运行,则它们将失败。

我知道我可以使用--test-threads=1来运行所有程序,但是我发现这对一些特殊测试来说很不方便。

有什么办法可以保持并行运行所有测试,并且有少数例外?理想情况下,我想说不要同时运行X,Y,Z。

3 个答案:

答案 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应该很好...),则可以处理中毒:

()

如果您需要能够并行运行一组有限的线程,则可以使用信号灯。在这里,我使用Condvarlet _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=1cargo test --test parallel。因此,它不适用于单个cargo test,但是您不需要编写自己的测试工具逻辑。

答案 2 :(得分:1)

使用serial_test条板箱。添加了这个箱子之后,您就可以输入代码了:

#[serial]

要依次运行的任何测试之前。