Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions docs/rust-testing-with-rstest-fixtures.md
Original file line number Diff line number Diff line change
Expand Up @@ -853,6 +853,16 @@ Good fixture and test organization mirrors good software design principles. As t
- **Scope Management (**`#[once]` **vs. Regular):** Make conscious decisions about fixture lifetimes. Use `#[once]` sparingly, only for genuinely expensive, read-only, and safely static resources, being mindful of its "never dropped" nature.12 Prefer regular (per-test) fixtures for test isolation and proper resource management.
- **Modularity:** Group related fixtures and tests into modules. This improves navigation and understanding of the test suite.
- **Readability:** Utilize features like `#[from]` for renaming 12 and `#[default]` / `#[with]` for configurable fixtures to enhance the clarity of both fixture definitions and their usage in tests.
- **Utility Macros:** The integration tests define a `lines_vec!` macro for
quickly building `Vec<String>` from string slices. Use it in fixtures to
avoid repetitive `.to_string()` calls.

```rust
#[fixture]
fn example_table() -> Vec<String> {
lines_vec!("a", "b", "c")
}
```

General testing advice, such as keeping tests small and focused and mocking external dependencies 17, also applies and is well-supported by `rstest`'s design.

Expand Down
7 changes: 7 additions & 0 deletions tests/common/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/// Utility helpers shared across integration tests.

macro_rules! lines_vec {
($($line:expr),* $(,)?) => {
vec![$($line.to_string()),*]
};
}
110 changes: 47 additions & 63 deletions tests/integration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ use std::fs::File;
use std::io::Write;
use tempfile::tempdir;

#[macro_use]
mod common;

#[fixture]
/// Provides a sample Markdown table with broken rows for testing purposes.
///
Expand All @@ -17,10 +20,7 @@ use tempfile::tempdir;
/// assert_eq!(table[0], "| A | B | |");
/// ```
fn broken_table() -> Vec<String> {
vec![
"| A | B | |".to_string(),
"| 1 | 2 | | 3 | 4 |".to_string(),
]
lines_vec!("| A | B | |", "| 1 | 2 | | 3 | 4 |",)
}

#[fixture]
Expand All @@ -35,113 +35,97 @@ fn broken_table() -> Vec<String> {
/// assert_eq!(table, vec![String::from("| A | |"), String::from("| 1 | 2 | 3 |")]);
/// ```
fn malformed_table() -> Vec<String> {
vec!["| A | |".to_string(), "| 1 | 2 | 3 |".to_string()]
lines_vec!("| A | |", "| 1 | 2 | 3 |")
}

#[fixture]
fn header_table() -> Vec<String> {
vec![
"| A | B | |".to_string(),
"| --- | --- |".to_string(),
"| 1 | 2 | | 3 | 4 |".to_string(),
]
lines_vec!("| A | B | |", "| --- | --- |", "| 1 | 2 | | 3 | 4 |",)
}

#[fixture]
fn escaped_pipe_table() -> Vec<String> {
vec![
"| X | Y | |".to_string(),
"| a \\| b | 1 | | 2 | 3 |".to_string(),
]
lines_vec!("| X | Y | |", "| a \\| b | 1 | | 2 | 3 |",)
}

#[fixture]
fn indented_table() -> Vec<String> {
vec![
" | I | J | |".to_string(),
" | 1 | 2 | | 3 | 4 |".to_string(),
]
lines_vec!(" | I | J | |", " | 1 | 2 | | 3 | 4 |",)
}

#[fixture]
fn html_table() -> Vec<String> {
vec![
"<table>".to_string(),
"<tr><th>A</th><th>B</th></tr>".to_string(),
"<tr><td>1</td><td>2</td></tr>".to_string(),
"</table>".to_string(),
]
lines_vec!(
"<table>",
"<tr><th>A</th><th>B</th></tr>",
"<tr><td>1</td><td>2</td></tr>",
"</table>",
)
}

#[fixture]
fn html_table_with_attrs() -> Vec<String> {
vec![
"<table class=\"x\">".to_string(),
"<tr><th>A</th><th>B</th></tr>".to_string(),
"<tr><td>1</td><td>2</td></tr>".to_string(),
"</table>".to_string(),
]
lines_vec!(
"<table class=\"x\">",
"<tr><th>A</th><th>B</th></tr>",
"<tr><td>1</td><td>2</td></tr>",
"</table>",
)
}

#[fixture]
fn html_table_with_colspan() -> Vec<String> {
vec![
"<table>".to_string(),
"<tr><th colspan=\"2\">A</th></tr>".to_string(),
"<tr><td>1</td><td>2</td></tr>".to_string(),
"</table>".to_string(),
]
lines_vec!(
"<table>",
"<tr><th colspan=\"2\">A</th></tr>",
"<tr><td>1</td><td>2</td></tr>",
"</table>",
)
}

#[fixture]
fn html_table_no_header() -> Vec<String> {
vec![
"<table>".to_string(),
"<tr><td>A</td><td>B</td></tr>".to_string(),
"<tr><td>1</td><td>2</td></tr>".to_string(),
"</table>".to_string(),
]
lines_vec!(
"<table>",
"<tr><td>A</td><td>B</td></tr>",
"<tr><td>1</td><td>2</td></tr>",
"</table>",
)
}

#[fixture]
fn html_table_empty() -> Vec<String> {
vec!["<table></table>".to_string()]
lines_vec!("<table></table>")
}

#[fixture]
fn html_table_unclosed() -> Vec<String> {
vec!["<table>".to_string(), "<tr><td>1</td></tr>".to_string()]
lines_vec!("<table>", "<tr><td>1</td></tr>")
}

#[fixture]
fn html_table_uppercase() -> Vec<String> {
vec![
"<TABLE>".to_string(),
"<tr><th>A</th><th>B</th></tr>".to_string(),
"<tr><td>1</td><td>2</td></tr>".to_string(),
"</TABLE>".to_string(),
]
lines_vec!(
"<TABLE>",
"<tr><th>A</th><th>B</th></tr>",
"<tr><td>1</td><td>2</td></tr>",
"</TABLE>",
)
}

#[fixture]
fn html_table_mixed_case() -> Vec<String> {
vec![
"<TaBlE>".to_string(),
"<tr><th>A</th><th>B</th></tr>".to_string(),
"<tr><td>1</td><td>2</td></tr>".to_string(),
"</TaBlE>".to_string(),
]
lines_vec!(
"<TaBlE>",
"<tr><th>A</th><th>B</th></tr>",
"<tr><td>1</td><td>2</td></tr>",
"</TaBlE>",
)
}

#[fixture]
fn multiple_tables() -> Vec<String> {
vec![
"| A | B |".to_string(),
"| 1 | 22 |".to_string(),
String::new(),
"| X | Y |".to_string(),
"| 3 | 4 |".to_string(),
]
lines_vec!("| A | B |", "| 1 | 22 |", "", "| X | Y |", "| 3 | 4 |",)
}

#[rstest]
Expand Down