# Testing Strategy
Every public function must have comprehensive test coverage.
## 10.1 Unit Tests (inline per module)
Every public function has at least:
- A happy-path test
- An edge case (empty string, zero, negative numbers, Unicode input)
- A `#[should_panic]` or `Err` assertion where appropriate
```rust
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_to_snake_case_basic() {
assert_eq!(to_snake_case("HelloWorld"), "hello_world");
assert_eq!(to_snake_case("XMLParser"), "xml_parser");
assert_eq!(to_snake_case("TestV2"), "test_v2");
assert_eq!(to_snake_case(""), "");
}
#[test]
fn test_truncate_complete_words() {
let s = "This is a very long title";
assert_eq!(truncate(s, 10, true, "..."), "This is a...");
assert_eq!(truncate(s, 10, false, "..."), "This is a ...");
}
#[test]
fn test_slug_unicode() {
assert_eq!(slug("hello ♥ world", '-'), "hello-love-world");
}
}
```
## 10.2 Property-Based Tests (`proptest`)
```rust
use proptest::prelude::*;
proptest! {
/// round-trip: snake → camel → snake should be stable
#[test]
fn prop_snake_camel_roundtrip(s in "[a-z][a-z0-9]{0,20}(_[a-z][a-z0-9]{0,10})*") {
let result = to_snake_case(&to_camel_case(&s));
prop_assert_eq!(result, s);
}
/// truncate never returns more chars than the limit
#[test]
fn prop_truncate_length(s in ".*", limit in 0usize..200) {
let result = truncate(&s, limit, false, "");
prop_assert!(result.chars().count() <= limit + 3); // +3 for suffix
}
/// unique preserves all elements that appeared at least once
#[test]
fn prop_unique_subset(arr in prop::collection::vec(0i32..100, 0..50)) {
let result = unique(&arr);
for x in &result {
prop_assert!(arr.contains(x));
}
}
}
```
## 10.3 Benchmarks (Criterion)
```rust
// benches/string_bench.rs
use criterion::{criterion_group, criterion_main, Criterion};
use rok_utils::str::*;
fn bench_slug(c: &mut Criterion) {
let input = "The Quick Brown Fox Jumps Over The Lazy Dog";
c.bench_function("slug", |b| b.iter(|| slug(input, '-')));
}
fn bench_fluent_chain(c: &mut Criterion) {
c.bench_function("fluent_chain", |b| {
b.iter(|| {
Str::of(" Hello World ")
.trim()
.slug()
.truncate(30)
.value()
})
});
}
criterion_group!(benches, bench_slug, bench_fluent_chain);
criterion_main!(benches);
```
## 10.4 Doctests as Living Docs
Every public function has a `/// # Examples` block that doubles as a doctest:
```rust
/// Convert a string to snake_case.
///
/// # Examples
///
/// ```rust
/// use rok_utils::str::to_snake_case;
///
/// assert_eq!(to_snake_case("HelloWorld"), "hello_world");
/// assert_eq!(to_snake_case("XMLParser"), "xml_parser");
/// assert_eq!(to_snake_case(""), "");
/// ```
pub fn to_snake_case(s: &str) -> String { ... }
```