如何检查Rust中是否已调用函数?

时间:2019-02-07 21:41:45

标签: unit-testing rust

我具有以下功能

pub fn registration(student_id: &T::StudentId, registrar: &T::RegistrarID) {
    // More code here.
    if num_of_students < student_limit {
        Self::function_one(&registrar, &num_of_students);
    } else {
        Self::function_two(&num_of_students);
    }
}

在单元测试中,我计划检查是否调用了function_onefunction_two

#[test]
fn registration_more_students_should_call_functon_one() {
    with_test_data(
        &mut TestBuilder::default().num_of_students(1000).build(),
        || {
            //assert_called!(module_name::registration("TV:009", "DF-000-09"));
        },
    );
}

如何测试是否在Rust中调用了函数?

2 个答案:

答案 0 :(得分:8)

强烈建议:您的测试错误。这与“我如何测试私有方法”处于同一级别。您不必关心registration的实现细节。

话虽如此,如果知道采用哪个if分支实际上很重要,那么请使用依赖注入:

fn registration(mut registration: impl Registration, registrar: i32) {
    let num_of_students = 0;
    let student_limit = 0;

    if num_of_students < student_limit {
        registration.function_one(registrar, num_of_students);
    } else {
        registration.function_two(num_of_students);
    }
}

trait Registration {
    fn function_one(&mut self, registrar: i32, num_of_students: i32);
    fn function_two(&mut self, num_of_students: i32);
}

impl<R: Registration> Registration for &'_ mut R {
    fn function_one(&mut self, registrar: i32, num_of_students: i32) {
        (**self).function_one(registrar, num_of_students)
    }
    fn function_two(&mut self, num_of_students: i32) {
        (**self).function_two(num_of_students)
    }
}

/*
// An example implementation for production
struct DatabaseRegistration;

impl Registration for DatabaseRegistration {
    fn function_one(&mut self, registrar: i32, num_of_students: i32) {
        eprintln!("Do DB work: {}, {}", registrar, num_of_students)
    }
    fn function_two(&mut self, num_of_students: i32) {
        eprintln!("Do DB work: {}", num_of_students)
    }
}
*/

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

    #[derive(Debug, Copy, Clone, Default)]
    struct TestRegistration {
        calls_to_one: usize,
        calls_to_two: usize,
    }

    impl Registration for TestRegistration {
        fn function_one(&mut self, _: i32, _: i32) {
            self.calls_to_one += 1;
        }
        fn function_two(&mut self, _: i32) {
            self.calls_to_two += 1;
        }
    }

    #[test]
    fn calls_the_right_one() {
        let mut reg = TestRegistration::default();
        registration(&mut reg, 42);
        assert_eq!(1, reg.calls_to_two)
    }
}

完成此操作后,您可以看到registration调用了适当的trait函数(如示例测试中所示)。

这可以防止在生产代码中散布特定于测试的碎屑,同时还使您能够更灵活地快速测试更多案例。

另请参阅:

答案 1 :(得分:4)

这是在多个地方简单使用#[cfg(test)]的尝试:

struct Registration {
    students: Vec<String>,
    #[cfg(test)]
    function_1_called: bool,
}

impl Registration {
    fn new() -> Self {
        Registration {
            students: Vec::new(),
            #[cfg(test)]
            function_1_called: false,
        }
    }

    fn function_1(&mut self, students: Vec<String>) {
        self.students.extend(students);
        #[cfg(test)]
        {
            self.function_1_called = true;
        }
    }

    fn function_2(&mut self, students: Vec<String>) {}

    fn f(&mut self, students: Vec<String>) {
        if students.len() < 100 {
            self.function_1(students);
        } else {
            self.function_2(students);
        }
    }
}

fn main() {
    println!("Hello, world!");
    let r = Registration::new();
    // won't compile during `run`:
    // println!("{}", r.function_1_called);
}

#[cfg(test)]
mod test {
    use super::*;
    #[test]
    fn test_f() {
        let mut r = Registration::new();
        r.function_1(vec!["Alice".to_string(), "Bob".to_string()]);
        assert!(r.function_1_called);
    }
}

该代码大致基于您提供的代码片段。有一个Registration结构,该结构包含一个学生姓名列表,两个用于注册学生的方法function_1function_2,以及一个在f之间进行选择的函数function_1function_2取决于有多少学生。

在测试过程中,Registration使用附加的布尔变量function_1_called进行编译。仅当调用function_1时才设置此变量(执行此操作的代码块也标有#[cfg(test)])。

结合起来,这将在测试期间提供一个附加的布尔标志,以便您可以进行如下声明:

assert!(r.function_1_called);

显然,这可以用于比单个布尔标志复杂得多的结构(这并不意味着您实际上应该这样做)。

我无法评论这在Rust中是否是惯用的。整个设置看起来好像应该隐藏在花哨的宏后面,因此,如果完全在Rust中使用这种测试风格,则应该已经有提供这些(或类似)宏的板条箱。