This post will discuss how to parameterize tests in Rust. Qxf2 likes the fact that Rust makes testing a first class citizen of the language. It provides a test runner out of the box. However, coming from Python land, we missed one feature – test parametrization. So went about looking for crates that could help. We have found rstest to be a good framework to start with to parameterize our tests initially.
Problem Statement
We were happily practicing writing some unit tests for key-value store using SQLite. Qxf2 is in no way involved with that project – we just used it for practice since it seemed new and changing. We chose to write a test for creating a table with invalid names. The test structure would look like something below
fn test_create_table_invalid_names() -> Result<(), Error>{ let table_name = "Something invalid here" let connection = Connection::open_in_memory()?; let table = Table::new(table_name); let result = table.create(&connection); assert_eq!(result.is_err(), expected_output, "{}", explanation); Ok(()) } |
The above is a function from our unit test where we were creating table names with invalid names. Being testers we could imagine several examples of invalid names. We obviously do not want to write one test function for each invalid name. So we thought of parameterizing the unit tests.
Looking up online, we didn’t find any good articles (written in Mar 2023) showing us how to parameterize tests in Rust. So, we thought to do the groundwork ourselves and share it with the larger group.
Install rstest
To use rstest, we should either install or add it in Cargo.toml
To install, Run the following Cargo command in your project directory:
cargo add rstest |
OR
Add the following line to your Cargo.toml:
rstest = "0.17.0" |
Once installed, add the following to your test file.
use rstest::rstest; |
How to use rstest in your tests
For folks that want to get to the meat of the matter, this is how you use rstest. You add an #[rstest] attribute and list the cases you want.
#[rstest( table_name, expected_output, explanation, case::special_character("@@@@@#####%%%&&&&", true, "Invalid table name with special characters"), case::space("table name with spaces", true, "Invalid table name with space"), case::end_with_special_character("table_name_with_special_characters_#&%", true, "Invalid table name ending with special characters"), case::start_with_special_character("@#$%^&starts_with_special_character", true, "Invalid table name starting with special characters") )] |
This is how rstest can be used to use various cases for your tests. In our case, we were trying to pick some cases for invalid database table names.
The Test Function for creating a table with Invalid name
The complete code snippet looks something like this. Note that the test function signature has changed to have three new arguments -> table_name, expected_output and explanation. These map to the three arguments within case::blah(). We can now use whatever we had listed within case::blah() as variables within out test.
One point to note is that we like the format case::
#[rstest( table_name, expected_output, explanation, case::special_character("@@@@@#####%%%&&&&", true, "Invalid table name with special characters"), case::space("table name with spaces", true, "Invalid table name with space"), case::end_with_special_character("table_name_with_special_characters_#&%", true, "Invalid table name ending with special characters"), case::start_with_special_character("@#$%^&starts_with_special_character", true, "Invalid table name starting with special characters") )] fn test_create_table_invalid_names(table_name: &str, expected_output: bool, explanation: &str) -> Result<(), Error> { let connection = Connection::open_in_memory()?; let table = Table::new(table_name); let result = table.create(&connection); assert_eq!(result.is_err(), expected_output, "{}", explanation); Ok(()) } |
Decode the test
For folks completely new to Rust, here is a short explanation. In the above test, we tried to do the following:
1. Open a SQLite database connection in memory
2. Create a new table in the database
3. The new table takes the table name from the cases provided within rstest case
4. Create a connection with the newly created table
5. Assert to check that the create() method does raise an error when provided an invalid table name
Hire Qxf2 testers for your next Rust project
Do you have Rust projects that need testers? Qxf2 has the technical testers you are looking for. Our testers go well beyond traditional test automation. We are engineers who test well. Reach out to us here.
My journey in software testing began with Calabash and Cucumber, where I delved into Mobile Automation using Ruby. As my career progressed, I gained experience working with a diverse range of technical tools. These include e-Discovery, Selenium, Python, Docker, 4G/5G testing, M-CORD, CI/CD implementation, Page Object Model framework, API testing, Testim, WATIR, MockLab, Postman, and Great Expectation. Recently, I’ve also ventured into learning Rust, expanding my skillset further. Additionally, I am a certified Scrum Master, bringing valuable agile expertise to projects.
On the personal front, I am a very curious person on any topic. According to Myers-Briggs Type Indicator, I am described as INFP-T. I like playing soccer, running and reading books.