首页 > 解决方案 > 如何在不要求参数函数可变的情况下测试元函数?

问题描述

我编写了一个save以函数为参数的函数:

fn save(method:&dyn Fn(&'static str)) {
    method("Hello world");
}

fn print(string:&'static str) {
    println!("{}", string);
}

fn main() {
    save(&print)
}

这很好用!但我现在想测试save。我认为这样做的最好方法是使用FnMut

fn save(method: &mut dyn FnMut(&'static str)) {
    method("Hello world");
}

fn print(string: &'static str) {
    println!("{}", string);
}

fn main() {
    save(&mut print)
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn save_test() {
        let actual = {
            let mut actual = String::new();
            let mut method = |string: &'static str| {
                actual = format!("{}{}", actual, string);
            };
            save(&mut method);
            save(&mut method);
            actual
        };
        let expected = "Hello worldHello world".to_string();

        assert_eq!(actual, expected);
    }
}

这仍然有效并完成了我想要的一切!但是现在我每次call保存时都必须使用可变引用。虽然这不会影响功能,但它会混淆代码。有没有更好的方法来实现相同的结果?

标签: testingrustautomated-testshigher-order-functions

解决方案


您可以使用RefCell来获得内部可变性,允许您通过共享引用来可变变量。它非常适合测试这样的事情:

#[cfg(test)]
mod tests {
    use std::cell::RefCell;
    use super::*;

    #[test]
    fn save_test() {
        let actual = {
            // wrap "actual" in a RefCell, which allows for interior mutability
            let actual = RefCell::new(String::new());
            let method = |string: &'static str| {
                // mutably borrow the string at runtime
                // (can panic in already borrowed, but not a problem here)
                let mut actual = actual.borrow_mut();

                // append string (equivalent to your format!() but can be more
                // efficient)
                actual.push_str(string);
            };

            save(&method);
            save(&method);

            // move string out of RefCell
            actual.into_inner()
        };
        let expected = "Hello worldHello world".to_string();

        assert_eq!(actual, expected);
    }
}

在操场上奔跑


推荐阅读