代码之家  ›  专栏  ›  技术社区  ›  not 0x12

如何检查函数是否在rust中被调用?

  •  1
  • not 0x12  · 技术社区  · 5 年前

    我的功能如下

    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_one function_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 回复  |  直到 5 年前
        1
  •  4
  •   Andrey Tyukin    5 年前

    这是一个天真的尝试 #[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_1 function_2 用于注册学生,以及一个函数 f 在两者之间选择 功能1 功能2 取决于有多少学生。

    在测试过程中, 登记处 使用附加的布尔变量编译 function_1_called . 仅当 功能1 被调用(执行此操作的代码块也被标记为 γ[CFG(测试)] )

    结合起来,这使得在测试期间有一个额外的布尔标记可用,这样您就可以做出如下断言:

    assert!(r.function_1_called);
    

    显然,这对于比单个布尔标记复杂得多的结构来说是可行的(这并不意味着您实际上应该这样做)。

    我不能评论这是否是铁锈成语。整个设置感觉好像应该隐藏在华丽的宏之后,所以如果这种类型的测试在铁锈中使用,就应该已经有了提供那些(或类似)宏的板条箱。

        2
  •  7
  •   Andrey Tyukin    5 年前

    强烈的意见提醒: 你的测试做得不对 . 这与“如何测试私有方法”的级别相同。你不应该关心 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)
        }
    }
    

    一旦你这样做了,你就可以看到 注册 调用适当的特征函数(如示例测试中所示)。

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

    参见: