MST

星途 面试题库

面试题:Rust单元测试中的Mock与Stub

假设你有一个Rust模块,其中一个函数`fetch_data`依赖外部API调用获取数据,然后另一个函数`process_data`处理`fetch_data`获取的数据。在对`process_data`进行单元测试时,由于`fetch_data`实际调用外部API成本高且不稳定,如何使用Mock或Stub技术来隔离`fetch_data`的依赖,从而实现对`process_data`的可靠单元测试?请写出具体实现代码,并解释Mock和Stub的区别以及适用场景。
35.1万 热度难度
编程语言Rust

知识考点

AI 面试

面试题答案

一键面试

实现代码

  1. 定义模块和函数
// src/lib.rs
pub fn fetch_data() -> String {
    // 实际这里会调用外部API,现在简单返回一个字符串
    "real data from api".to_string()
}

pub fn process_data() -> String {
    let data = fetch_data();
    // 简单处理数据,比如将字符串转大写
    data.to_uppercase()
}
  1. 使用Mock技术进行单元测试
// src/lib.rs
// 引入依赖
#[cfg(test)]
mod tests {
    use super::*;
    use mockall::mock;

    mock! {
        DataFetcher {
            fn fetch_data(&self) -> String;
        }
    }

    #[test]
    fn test_process_data() {
        let mut mock_fetcher = MockDataFetcher::new();
        mock_fetcher.expect_fetch_data().returning(|| "mocked data".to_string());

        let result = {
            let old_fetch_data = std::mem::replace(
                &mut super::fetch_data,
                move || mock_fetcher.fetch_data(),
            );
            let res = process_data();
            std::mem::replace(&mut super::fetch_data, old_fetch_data);
            res
        };

        assert_eq!(result, "MOCKED DATA");
    }
}

Mock和Stub的区别以及适用场景

  1. 区别
    • Stub:Stub是为了提供固定的响应,通常是简单地返回预设的数据,而不关心函数被调用的次数、参数等细节。Stub主要是为了让被测代码能够顺利运行,提供必要的输入数据。例如,在上述代码中,如果使用Stub,可能只是简单地替换fetch_data函数返回一个固定字符串,而不验证调用情况。
    • Mock:Mock不仅返回预设的数据,还可以验证函数是否被调用、调用次数、调用时的参数等。Mock更侧重于验证被测代码与依赖之间的交互是否正确。在上述测试代码中,MockDataFetcher不仅返回了预设字符串,还可以通过expect_fetch_data等方法验证fetch_data是否按预期被调用。
  2. 适用场景
    • Stub适用场景:当我们只关心被测函数的输出是否正确,而不关心它与依赖的交互细节时,使用Stub。例如,一个函数只是简单地读取某个数据源的数据并进行计算,我们只需要提供固定数据来测试计算逻辑,这时Stub就足够了。
    • Mock适用场景:当我们需要验证被测函数与依赖之间的交互行为是否符合预期时,使用Mock。比如,一个函数需要根据不同条件调用不同的外部API,我们需要验证在不同条件下API是否被正确调用,此时Mock更合适。