You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Rust中使用mockall库Mock libc的shm_open函数?

在Rust中使用mockall拦截libc函数实现灵活Mock

问题背景

在Rust代码中调用了libc的多个函数(以shm_open为例),希望在测试用例中拦截这些调用并使用自定义Mock函数,实现不同测试用例返回不同结果。目前已实现返回固定值的stub函数,但尝试用mockall库时出现报错(无法直接导入dummy_libc的shm_open),需要正确的实现方式。

原stub实现代码:

use libc::{c_int, c_char, size_t, mode_t};
use libc::{O_RDWR, S_IROTH, S_IRUSR, S_IWOTH, S_IWUSR};

use std::mem;
#[cfg(not(test))]
use libc::shm_open;
#[cfg(test)]
fn shm_open(
    name: *const c_char, 
    oflag: i32, 
    mode: u32
) -> c_int{
    42 as c_int
}
const SHM_NAME: *const c_char = b"/ptp\0".as_ptr() as *const c_char;
const SHM_SIZE: size_t = mem::size_of::<u32>();
struct Consumer {
    s_value: c_int,
    s_data: u32,
}

trait GuestConsumer{
    fn new() -> Self;
}

impl GuestConsumer for Consumer {
    fn new() -> Self {
        let shm_fd = unsafe {
            shm_open(
                SHM_NAME,
                O_RDWR,
                (S_IRUSR | S_IROTH | S_IWUSR | S_IWOTH) as mode_t,
            )
        };
        Self {
            s_value: shm_fd,
            s_data: 0
        }
    }
}

#[cfg(test)]
mod tests{
    use super::*;
    #[test]
    fn test_new(){
        let value  = Consumer::new();
        println!("s_value: {:?}", value.s_value);
    }
}


fn main() {
    println!("Hello, world!");
}

尝试的错误代码(报错点:无法直接导入dummy_libc的shm_open):

use mockall::*;
use mockall::predicate::*;

#[cfg(not(test))]
use libc::shm_open;
#[cfg(test)]
use dummy_libc::shm_open;    // error: not directly importable

#[allow(dead_code, non_camel_case_types)]
#[cfg(test)]
#[automock]
pub trait dummy_libc{
    fn shm_open(
        name: *const c_char, 
        oflag: c_int, 
        mode: mode_t
    )-> c_int;
}

正确实现方案

核心思路是通过依赖注入和trait抽象解耦代码与libc函数,让测试时可以替换为mock实例:

  1. 定义抽象trait封装需要mock的libc函数
  2. 为生产环境实现该trait,绑定真实的libc调用
  3. 修改Consumer,使其依赖该trait而非直接调用libc函数
  4. 在测试中使用mockall生成的Mock对象,设置不同测试用例的返回值

完整修正代码

use libc::{c_int, c_char, mode_t, O_RDWR, S_IROTH, S_IRUSR, S_IWOTH, S_IWUSR};
use std::mem;

// 1. 定义抽象trait,封装需要mock的libc函数
#[cfg_attr(test, automock)]
pub trait ShmOps {
    unsafe fn shm_open(
        name: *const c_char,
        oflag: c_int,
        mode: mode_t,
    ) -> c_int;
}

// 2. 生产环境实现:调用真实的libc::shm_open
#[cfg(not(test))]
pub struct RealShmOps;

#[cfg(not(test))]
impl ShmOps for RealShmOps {
    unsafe fn shm_open(
        name: *const c_char,
        oflag: c_int,
        mode: mode_t,
    ) -> c_int {
        libc::shm_open(name, oflag, mode)
    }
}

const SHM_NAME: *const c_char = b"/ptp\0".as_ptr() as *const c_char;
const SHM_SIZE: usize = mem::size_of::<u32>();

struct Consumer {
    s_value: c_int,
    s_data: u32,
}

impl Consumer {
    // 3. 修改构造函数,接收ShmOps实例作为参数(依赖注入)
    pub fn new<T: ShmOps>(shm_ops: T) -> Self {
        let shm_fd = unsafe {
            shm_ops.shm_open(
                SHM_NAME,
                O_RDWR,
                (S_IRUSR | S_IROTH | S_IWUSR | S_IWOTH) as mode_t,
            )
        };
        Self {
            s_value: shm_fd,
            s_data: 0,
        }
    }
}

// 生产环境的便捷构造函数
#[cfg(not(test))]
impl Consumer {
    pub fn default() -> Self {
        Self::new(RealShmOps)
    }
}

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

    #[test]
    fn test_new_returns_fd_42() {
        // 创建Mock实例并设置预期返回值
        let mut mock = MockShmOps::new();
        mock.expect_shm_open()
            .with(eq(SHM_NAME), eq(O_RDWR), eq((S_IRUSR | S_IROTH | S_IWUSR | S_IWOTH) as mode_t))
            .returning(|_, _, _| 42);

        let consumer = Consumer::new(mock);
        assert_eq!(consumer.s_value, 42);
    }

    #[test]
    fn test_new_returns_error() {
        // 另一个测试用例,返回错误值-1
        let mut mock = MockShmOps::new();
        mock.expect_shm_open()
            .returning(|_, _, _| -1);

        let consumer = Consumer::new(mock);
        assert_eq!(consumer.s_value, -1);
    }
}

fn main() {
    #[cfg(not(test))]
    {
        let _consumer = Consumer::default();
        println!("Hello, world!");
    }
}

关键改动说明

  • 抽象trait:ShmOps封装了shm_open函数,#[cfg_attr(test, automock)]让mockall在测试环境自动生成MockShmOps结构体。
  • 依赖注入:Consumer::new接收ShmOps实例,不再直接硬编码调用libc::shm_open,实现了代码解耦。
  • 生产环境绑定:RealShmOps实现ShmOps,调用真实的libc函数,同时提供default构造函数方便生产代码使用。
  • 测试灵活Mock:每个测试用例可以创建独立的MockShmOps实例,通过expect_shm_open设置不同的参数匹配规则和返回值,满足不同测试场景需求。

内容的提问来源于stack exchange,提问作者Gagan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 10:29:51