app.rs
Let’s start with the same struct
as we had before:
/// Application.
#[derive(Debug, Default)]
pub struct App {
/// should the application exit?
pub should_quit: bool,
/// counter
pub counter: u8,
}
We can add additional methods to this Application
struct:
impl App {
/// Constructs a new instance of [`App`].
pub fn new() -> Self {
Self::default()
}
/// Handles the tick event of the terminal.
pub fn tick(&self) {}
/// Set running to false to quit the application.
pub fn quit(&mut self) {
self.should_quit = true;
}
pub fn increment_counter(&mut self) {
if let Some(res) = self.counter.checked_add(1) {
self.counter = res;
}
}
pub fn decrement_counter(&mut self) {
if let Some(res) = self.counter.checked_sub(1) {
self.counter = res;
}
}
}
We use the principle of encapsulation to expose an interface to modify the state. In this particular instance, it may seem like overkill but it is good practice nonetheless.
The practical advantage of this is that it makes the state changes easy to test.
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_app_increment_counter() {
let mut app = App::default();
app.increment_counter();
assert_eq!(app.counter, 1);
}
#[test]
fn test_app_decrement_counter() {
let mut app = App::default();
app.decrement_counter();
assert_eq!(app.counter, 0);
}
}
You can test a single function by writing out fully qualified module path to the test function, like so:
cargo test -- app::tests::test_app_increment_counter --nocapture
Or even test all functions that start with test_app_
by doing this:
cargo test -- app::tests::test_app_ --nocapture
The --nocapture
flag prints stdout and stderr to the console, which can help debugging tests.