Essence of Software Testing
Software grows. In order to not sacrifice quality as the software grows, we need rigorous software testing.
Regression
There's a chance that the new code you wrote introduced defects in the system, this is called regression. If your codebase doesn't have high automation coverage than these defects could take a lot of time to be discovered and it will take even more time to detect which code changes introduced the defect.
Types of testing
Unit testing
'Unit' refers to a small isolated part of a program. It could be an individual function of a module or method of an object. Unit tests rely on mocking/stubbing(fake implementaitons). If your unit of code relies on external code, then you can "mock" that code so you can focus on testing the logic of the function.
Integration Testing
'Integration" testing is done on a component or a service of an application. A component or service can be a set of classes, methods or functions.
UI Testing
Applications are designed for "Users". User Interface testing is used to interact with UI elements and see if these elements behave as expected.
Testing in Rust
Rust provides a build-in framework for testing. Add ``#[test]` attribute to your test function and `cargo test`` will detect and execute your tests.
There are three ways of writing tests in Rust -
- Embedded test module
- External test folder
- Doc tests
This is the library we will be testing. It has two private and one public API.
pub fn positive_prime(num: i32) -> bool {
if is_positive(num) {
return is_prime(num);
}
false
}
fn is_positive(num: i32) -> bool {
if num > 0 {
return true;
}
false
}
fn is_prime(num: i32) -> bool {
for i in 2..num/2 {
if num % i == 0 {
return false
}
}
true
}
Embedded test module
Embedded tests modules are a part the module you are testing. By adding ``#[cfg(test)]`` annotation, you can specify testing part of your module. Embedded test modules are used for unit testing because they have access to all the private structs, enums, fields, and functions. Here's an example of how you can write test for an function that tests prime numbers -
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_prime_number() {
let test_set = vec![
(2, true),
(6, false),
(17, true),
(25, false)
];
for (num, actual) in test_set {
let result = is_prime(num);
assert_eq!(actual, result);
}
}
}
cargo test
Compiling my_test_app v0.1.0 (my_test_app)
Finished test [unoptimized + debuginfo] target(s) in 0.58s
Running unittests src\main.rs (target\debug\deps\my_test_app-3e8a404ff158ea59.exe)
running 1 test
test tests::test_prime_number ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
External tests folder
Tests written in External tests folder unlike module tests, only have access to public API, much like your code being used as a dependency. External test folders are used for writing integration tests. Here's an example of testing a public API ``positive_prime` which uses two private functions `is_positive` and `is_prime``.
extern crate my_test_app;
#[test]
fn check_positive_prime() {
let test_cases = vec![
(2, true),
(-2, false),
(15, false),
(17, true),
(-1, false),
];
for (num, actual) in test_cases {
let result = my_test_app::positive_prime(num);
assert_eq!(actual, result);
}
}
cargo test --test '*'
Finished test [unoptimized + debuginfo] target(s) in 0.00s
Running tests\integration.rs (target\debug\deps\integration-c6e95b0ecf5394d0.exe)
running 1 test
test check_positive_prime ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Doc tests
Examples written in documentation need to be tested as well. Often the documentation contains deprecated piece of code that doesn't work as expectred anymore. Rust allows wrting tests in documentation. Using triple backticks, you can write executatable code in documentaion.
/// Checks whether a number is positive
/// /// use my_test_app::is_positive; /// is_positive(1) == true; ///
pub fn is_positive(num: i32) -> bool {
if num > 0 {
return true;
}
false
}
cargo test --doc is_positive
Finished test [unoptimized + debuginfo] target(s) in 0.00s
Doc-tests my_test_app
running 1 test
test src\lib.rs - is_positive (line 9) ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.27s
Software grows. In order to not sacrifice quality as the software grows, we need rigorous software testing.