| name | writing-rust-tests |
| description | Use when writing, modifying, or reviewing Rust tests in crates/ or bindings/ - covers test macros, fixtures, assertions, WASM compatibility, and how to run tests |
Writing Rust Tests in libxmtp
Essentials
Always use #[xmtp_common::test] instead of #[test] or #[tokio::test] in crates/. It handles native/WASM dispatch and logging init.
#[xmtp_common::test(unwrap_try = true)]
async fn test_something() {
tester!(alix);
tester!(bo);
let group = alix.create_group(None, None)?;
group.invite(&bo).await?;
group.send_msg(b"hello").await;
bo.sync_welcomes().await?;
let bo_group = bo.group(&group.group_id)?;
let msg = bo_group.test_last_message_bytes().await?;
assert_eq!(msg.unwrap(), b"hello");
}
unwrap_try = true transforms ? into .unwrap() — use for tests returning ()
tester!(name) creates a registered test client with logging
group.invite(&other).await? and group.send_msg(b"msg").await from MlsGroupExt trait
Exception: bindings/mobile tests use #[tokio::test(flavor = "multi_thread")] directly (never targets WASM).
Running Tests
All just test and just wasm test variants pass extra args through to cargo nextest run:
just test
just test v3
just test v3 test_send_message
just test crate xmtp_mls
just wasm test
Tests that create clients need the backend: just backend up / just backend down
See Running Tests for nextest filter syntax, profiles, WASM/d14n targeting, and CI commands.
Writing Philosophy
- Test behavior, not implementation. Don't assert on internal state that could change.
- One assertion focus per test.
test_send_message shouldn't also verify group creation.
- Don't test the obvious. Skip trivial getters, derive impls, framework behavior.
- Don't duplicate coverage. If
test_talk_in_dm covers DM messaging, don't add test_dm_send_message.
- Use
tester! and convenience methods. Don't manually build clients.
- Prefer
unwrap_try = true. Eliminates noisy .unwrap() chains.
Further Reference
- Fixtures & tester! Macro — tester! options, chaining, convenience methods, TesterBuilder
- WASM Compatibility — cfg_attr, if_native!/if_wasm!, conditional compilation macros
- Assertions & Waiting — assert_ok!, assert_err!, wait_for_eq/ok/some/ge, custom assertion patterns
- Parametrized Tests — rstest, #[case], timeouts, attribute stacking
- Test Utilities — Data generators, logging, retry, TestLogReplace, toxiproxy
- Running Tests — Full nextest filter syntax, profiles, env vars, CI, backend services