0

我的库产生了一个侧线程,我正在尝试为它编写单元测试。如果图书馆/模拟恐慌,我想通过测试。

我在嘲笑mockall

我的库代码是这样的:

    #[cfg_attr(test, automock)]
    trait T1 { fn foo(&self, val: String) }
    #[cfg_attr(test, automock)]
    trait T2 { fn bar(&self, val: String) }
    #[cfg_attr(test, automock)]
    trait T3 { fn fa(&self, val: String) }
    
    struct MyLib<A: T1 + Send + Sync, B:T2 + Send + Sync, C:T3 + Send + Sync> {f1: Arc<A>, f2: Arc<B>, f3: Arc<C>};
    
    impl My_Lib {
      fn do_something (&self) {
        let f1 = Arc::clone(&self.f1);
        let f2 = Arc::clone(&self.f2);
        let f3 = Arc::clone(&self.f3);
        thread::Builder::new().name("lib_thread".to_string()).spawn(move || {
            // does something in an infinite loop with lots of logic and calls out to the traits which I am mocking in the tests
            loop {
               // some logic
               // ....
               f1.foo("something happened here which is interesting so check in test if it occured".to_string());
               // more logic
               // ...
               f2.bar("more interesting things happened here".to_string());
               // more logic
               // ...
               f3.fa("abc".to_string());
               thread::sleep(interval)
            }
        });
      }
    }
    
    #[test]
    fn test_lib() {
       let mut t1 = Mock_T1::new();
       t1.expect_foo().return_const(()); // all sorts of mocks...
       // all sorts of fancy logic
    
       let mut c = Mock_T3::new();
       c.expect_fa().withf(|x: String| { 
    // some complex assertion here which failed and it was called on "lib_thread"
          // i.e.
          assert!(x == "some interesting value which should be here!"); 
          assert(false); // should make the test fail but instead get a panic on the thread
// thread 'lib_thread' panicked at 'MockT3::fa: No matching expectation found
          // test is still running and never completes
       }
       //block the test from exiting until I want to finish
       std::thread::park();
    }

可重现的案例:

    fn should_fail() {
        std::thread::spawn(|| {
           loop {
               assert!(false); // test should complete here
               std::thread::sleep(Duration::from_secs(1));
           }
        });
    }

    #[test]
    fn fail() {
        should_fail();
        std::thread::park();
    }
4

1 回答 1

1

测试工具只检查它产生的线程是否恐慌,你必须将你自己的线程内的恐慌传播到调用测试的线程。为此,您可以将 a 返回JoinHandle到您的线程,该线程由以下人员创建thread::spawn

fn should_fail() -> std::thread::JoinHandle<()> {
    std::thread::spawn(|| {
        loop {
            assert!(false);
            std::thread::sleep(Duration::from_secs(1));
        }
    })
}

然后,您可以调用.join()手柄。JoinHandle::join()等待相关线程完成。如果子线程发生恐慌,Err则返回给定的参数panic!。通过这种方式,恐慌被传播到主线程:

#[test]
fn test() {
    let join_handle = should_fail();
    // unwrap will panic if `should_fail` panics
    join_handle.join().unwrap();
}

您可能不想JoinHandle仅出于测试目的而返回 。然而JoinHandle,远不止这些。事实上,甚至还有关于标记它的讨论#[must_use]这是一篇关于加入你的线程的优秀帖子。


还有其他几种方法可以等待线程在不阻塞的情况下完成,例如使用通道或此处讨论的引用计数器。

于 2020-11-01T15:31:33.110 回答