Tests
No matter how good we are, every new piece of code we add could be a new bug.
Rust differentiates three types of testing practices: unit testing, doc testing and integration testing. As the last two are meant for libraries, we're going to focus in the first one: Unit Testing.
Configuring Tests
There's nothing special we have to do to execute our unit tests. However, we're probably going to need to mock some of our app components in order to make them work. For this purpose Kalgan supports the feature test
which provides a collection of mocking tools.
This feature must be added in the [dev-dependencies] section of our Cargo.toml.
Notice that since Rust 1.51, Cargo supports a new feature resolver which can be activated with resolver = "2"
in our Cargo.toml. This new resolver assures that all features enabled on dev-dependencies will not be unified when those same dependencies are used as a normal dependency, unless those dev-dependencies are currently being built.
Therefore, our Cargo.toml should look as follows:
[package]
edition = "2021"
resolver = "2"
...
[dev-dependencies]
kalgan = { version = "0.9.0", features = ["test"] }
Be aware that test
feature should not be requested in [dependencies] as it's thought to be used just for testing and not for development or building purpose.
Mock Objects
In software testing mock objects are simulated components that reproduce the behavior of the real ones. When executing unit tests we need to mock the components used by the target function, otherwise the tests would fail. For example, when testing our controllers we're going to need to mock the Request
and Response
objects.
Kalgan provides a collection of functions to create these components and traits that the break the visibility restrictions of some of other Kalgan components.
Mocking app configuration
Routes, translation messages and settings parameters don't exist when executing unit tests. In order to create them Kalgan provides the following functions:
kalgan::mock_settings |
Creates the given settings parameters. |
kalgan::mock_routes |
Creates the route parameters for routing system. kalgan::mock_settings must be launched first. |
kalgan::mock_i18n |
Creates the message parameters for translation system. kalgan::mock_settings must be launched first. |
Mocking Request object
As we have already seen, Request
fields are private and can't be changed. Trator provides kalgan::http::request::Mock
trait which contains all setter methods.
Find the definition of this trait in the code below:
pub trait Mock<'a> {
fn mock() -> Self;
fn mock_set_method(self, method: String) -> Self;
fn mock_set_uri(self, uri: String) -> Self;
fn mock_set_protocol(self, protocol: String) -> Self;
fn mock_set_cookies(self, cookies: HashMap<String, String>) -> Self;
fn mock_set_host(self, host: String) -> Self;
fn mock_set_user_agent(self, user_agent: String) -> Self;
fn mock_set_input(self, input: HashMap<String, String>) -> Self;
fn mock_set_referer(self, referer: String) -> Self;
fn mock_set_files(self, files: HashMap<String, File<'a>>) -> Self;
fn mock_set_raw(self, raw: String) -> Self;
}
Mocking Response object
As with Request
object, Response
fields are private and can't be read. Trator provides kalgan::http::response::Mock
trait which contains all getter methods.
Find the definition of this trait in the code below:
pub trait Mock {
fn mock_get_status(self) -> String;
fn mock_get_content_type(self) -> String;
fn mock_get_location(self) -> String;
fn mock_get_cookies(self) -> Vec;
fn mock_get_content(self) -> String;
fn mock_get_content_length(self) -> String;
}
Creating Tests
Let's see how we can create a couple of tests for our controller.
Given the following controller::foo_controller::foobar
:
use kalgan::http::request::Request;
use kalgan::http::response::Response;
pub(crate) fn foobar(request: &Request) -> Response {
render!("foobar.html")
}
...
We're going to create two test cases: test_foobar_status and test_foobar_title. The former is for testing the response status code and the latter to test the <title> HTML block of the template. Notice we have to import traits kalgan::http::request::Mock
and kalgan::http::response::Mock
to break the visiblity of Request
and Response
objects, respectively.
Following Rust guidelines we must add our test block at the end of the file:
...
#[cfg(test)]
mod tests {
use super::*;
use kalgan::http::request::Mock as MockRequest;
use kalgan::http::response::Mock as MockResponse;
#[test]
fn test_foobar_status() {
kalgan::mock_settings("config/app");
let request = Request::mock();
let response = foobar(&request);
assert_eq!(response.mock_get_status(), "HTTP/1.1 200 OK");
}
#[test]
fn test_foobar_title() {
kalgan::mock_settings("config/app");
kalgan::mock_i18n();
let request = Request::mock();
let response = foobar(&request);
assert!(response.mock_get_content().contains("..."));
}
}
Finally we just have to execute the tests:
cargo test