Skip to main content

Testing with rb-sys-test-helpers

The rb-sys-test-helpers crate provides utilities for running Rust unit and integration tests within a live Ruby VM.

Setup

  1. Add dev-dependencies to Cargo.toml:

    [dev-dependencies]
    rb-sys-env = "0.1"
    rb-sys-test-helpers = "0.2"
  2. Activate rb-sys in build.rs:

    // build.rs
    fn main() -> Result<(), Box<dyn std::error::Error>> {
    rb_sys::activate()?;
    Ok(())
    }
  3. Annotate tests with #[ruby_test]: Use the #[ruby_test] attribute on test functions that interact with the Ruby API.

Example Test Module

A single test module can combine rb-sys, magnus, and proptest examples.

#[cfg(test)]
mod tests {
use rb_sys_test_helpers::ruby_test;
use rb_sys::{rb_num2fix, FIXNUM_P};
use magnus::{Ruby, RString};
use proptest::prelude::*;

#[ruby_test]
fn test_raw_rb_sys_api() {
let int = unsafe { rb_num2fix(123) };
assert!(FIXNUM_P(int));
}

#[ruby_test]
fn test_with_magnus() {
let ruby = unsafe { Ruby::get_unchecked() };
let string = ruby.str_new("Hello, Magnus!");
assert_eq!(string.to_string().ok(), Some("Hello, Magnus!".to_string()));
}

#[ruby_test]
fn test_with_proptest() {
proptest!(|(s in "[a-z]*")| {
let ruby = unsafe { Ruby::get_unchecked() };
let ruby_string = ruby.str_new(&s);
assert_eq!(ruby_string.to_string().ok(), Some(s));
});
}
}

Mechanism

  • #[ruby_test] initializes a Ruby VM before your test and tears it down afterward.
  • Allows unsafe calls to rb_sys or magnus::Ruby::get_unchecked() within the test scope.

Testing Multiple Ruby Versions

Use a CI matrix to run your cargo test suite against all supported Ruby versions.

# .github/workflows/ci.yml
jobs:
test:
strategy:
matrix:
ruby: ["2.7", "3.0", "3.1", "3.2", "3.3"]
steps:
- uses: actions/checkout@v4
- uses: oxidize-rb/actions/setup-ruby-and-rust@v1
with:
ruby-version: ${{ matrix.ruby }}
- run: cargo test