< 返回版块

ethanwang27 发表于 2024-11-10 18:57

我有一段代码的逻辑大概是这样的,在utils模块的generate_id里调用了random模块的random_odd_int生成一个ID。

我现在想要想一个测试方法只测试utils模块里generate_id是否正确,random_odd_int函数已经有专门的测试方法了。我该怎么来写这个测试方法在执行单元测试的时候,generate_id调用random_odd_int时调用的是random_odd_int的mock方法每次都返回一个固定值?

我尝试了mockall这个库,但没有成功,mockall设置random_odd_int的mock方法只有在测试方法里调用才起作用,generate_id函数里调用的random_odd_int还是random模块里的方法。

pub mod utils {
    use super::random::random_odd_int;
    
    pub fn generate_id() -> String {
        // 在这里调用random里的random_odd_int
        let seed = random_odd_int();
        format!("{:010}", seed)
    }
}


#[cfg(test)]
mod tests {
    use crate::utils::utils::generate_id;

    #[test]
    fn test_generate_id() {
        let excepted = "newid9091";
        // 在调用generate_id之前,我要怎样使用mock才能让random_odd_int返回固定值
        let actual = generate_id();
        assert_eq!(actual, excepted.to_string());
    }
}

评论区

写评论
liusen-adalab 2024-11-12 10:18

调整不了的吧,这两个互斥的条件,应该只能编译其中一个,也就是只有一个有跳转、refactor 之类的 ide 功能

一路火花带闪电 2024-11-11 23:00

你说的这几个在vscode中的问题,都可以通过调整vscode的配置,设置高亮的模式,是全部高亮,还是选择分支高亮。vscode的显示不是问题。 唯一需要考虑的是cfg这种方式,写多了,太乱,是不太优雅,哈哈。

liusen-adalab 2024-11-11 19:13

这种方式基本只能应急,因为有很多缺点,比如

  1. 在 vscode 中,rust-analyzer 默认是打开 test 的,所以只会高亮 #[cfg(tset)] 下的代码,实际环境中调用的代码反而失去 ide 支持
  2. 第一个缺点的延申,如果 #[cfg(not(test))] 下的代码使用了 use 导入的依赖,rust-analyzer 会报警告,因为依赖未使用,但实际编译不会有警告。虽然解决很简单,但看着很难受就是了
  3. 如果需要区分的地方多了,很容易写错,而且东一块西一块的,影响 review 代码
  4. 为了测试而侵入业务逻辑,这种做法不太优雅,当然,如果是应急,怎么来都行,有时我还会用 if false {} 代替注释,这样不会报警告,对强迫症非常友好,哈哈

所以总体来说,用条件编译有点得不偿失

--
👇
LazyBoy: 在不更改书写模式的情况下,大概也只有条件编译宏好用了,就是第一位老兄说的#[cfg(test)]

简单写的就是:

#[cfg(test)]
let seed = generate_odd_int();
#[cfg(not(test))]
let seed = CONST VALUE / FN;
SleepyBoy 2024-11-11 15:57

在不更改书写模式的情况下,大概也只有条件编译宏好用了,就是第一位老兄说的#[cfg(test)]

简单写的就是:

#[cfg(test)]
let seed = generate_odd_int();
#[cfg(not(test))]
let seed = CONST VALUE / FN;
liusen-adalab 2024-11-11 11:26

推荐使用依赖注入,对测试非常友好

pub struct Random;

impl utils::RandomOddInt for Random {
    fn random_odd_int() -> usize {
        todo!("这里是实际实现")
    }
}

pub mod utils {
    pub trait RandomOddInt {
        fn random_odd_int() -> usize;
    }

    pub fn generate_id<R: RandomOddInt>() -> String {
        // 在这里调用random里的random_odd_int
        let seed = R::random_odd_int();
        format!("{:010}", seed)
    }
}

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

    use super::utils::RandomOddInt;

    struct MockRand;

    impl RandomOddInt for MockRand {
        fn random_odd_int() -> usize {
            1
        }
    }

    #[test]
    fn test_generate_id() {
        let excepted = "newid9091";
        // 在调用generate_id之前,我要怎样使用mock才能让random_odd_int返回固定值
        let actual = generate_id::<MockRand>();
        assert_eq!(actual, excepted.to_string());
    }
}
asuper 2024-11-11 11:10

给一个不太美观的方法,用类似下面的方法来区分Test和非Test的代码,可以有多种用法,比如用Test标签来修饰use的部分,也可以在被调用的模块那边动手脚

#[test]
fn aaa() {
    println!("this is in test");
}

#[cfg(not(test))]
fn aaa() {
    println!("normal mode");
}
1 共 6 条评论, 1 页