3.7 Доказательство на своём месте
Тест, живущий отдельно от того, что он проверяет, остаётся тестом. Это принято называть организацией. Правильное слово - случайным соседством.
Что нужно
Четырнадцать тестов живут в src/main.rs - потому что когда они появились, других
файлов не было. Теперь есть. Тест задачи должен жить рядом с задачей, тест хранилища
- рядом с хранилищем.
Есть и практическая причина: тест как подмодуль видит приватные детали своего родителя.
Тест, живущий в main.rs, видит только то, что открыто наружу. Тест, живущий внутри
модуля - всё.
Переезд
В M3.6 модуль задачи - это файл src/task.rs. Чтобы дать ему подмодуль с тестами,
файл становится директорией: src/task.rs переименовывается в src/task/mod.rs.
Для компилятора и main.rs ничего не меняется - mod task; работает с обоими
вариантами одинаково.
В конец src/task/mod.rs добавляется одна строка:
// src/task/mod.rs - добавить в конец
#[cfg(test)]
mod tests;
#[cfg(test)] - атрибут условной компиляции: модуль tests включается только при
cargo test. mod tests; указывает компилятору на src/task/tests.rs.
Создаём src/task/tests.rs. Внутри - тело модуля tests:
// src/task/tests.rs - NEW
use super::*;
#[test]
fn new_task_starts_as_todo() {
let task = Task::new(1, "Buy coffee").unwrap();
assert!(!task.is_done());
}
#[test]
fn task_display_todo() {
let task = Task::new(1, "Buy coffee").unwrap();
assert_eq!(format!("{}", task), "#1: Buy coffee [Todo]");
}
По этому принципу оставшиеся семь тестов задачи переезжают туда же - каждый без изменений.
super здесь - модуль task. use super::* приносит все имена из task/mod.rs -
и публичные, и приватные: дочерний модуль видит всё что есть у родителя. Если в
task/mod.rs уже написано use std::fmt или use crate::error::TqError - в tests.rs
они доступны без повторного импорта. Именно поэтому тесты держат внутри, а не снаружи.
src/store.rs и src/error.rs переименовываются так же - в src/store/mod.rs и
src/error/mod.rs. Каждый получает #[cfg(test)] mod tests; в конце.
В src/store/tests.rs нужен дополнительный импорт: TqError живёт в другом модуле
и use super::* его не принесёт:
// src/store/tests.rs - NEW
use super::*;
use crate::error::TqError;
#[test]
fn store_assigns_sequential_ids() {
let mut store = TaskStore::new();
store.add("A").unwrap();
store.add("B").unwrap();
assert_eq!(store.all()[0].id, 1);
assert_eq!(store.all()[1].id, 2);
}
Оставшиеся два store-теста переезжают туда же.
Из src/main.rs весь блок #[cfg(test)] mod tests { ... } удаляется.
Результат
running 14 tests
test error::tests::error_display_empty_title ... ok
test error::tests::error_display_not_found ... ok
test store::tests::store_assigns_sequential_ids ... ok
test store::tests::store_get_mut_completes_task ... ok
test store::tests::store_get_returns_not_found ... ok
test task::tests::complete_marks_task_done ... ok
test task::tests::new_task_accepts_valid_title ... ok
test task::tests::new_task_rejects_empty_title ... ok
test task::tests::new_task_starts_as_todo ... ok
test task::tests::str_converts_into_task ... ok
test task::tests::task_display_done ... ok
test task::tests::task_display_todo ... ok
test task::tests::task_from_str_sets_title ... ok
test task::tests::task_id_is_preserved ... ok
test result: ok. 14 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
Четырнадцать тестов - те же, что и были. Только теперь каждый знает, где живёт.
make ci проходит.
Полный код
tqдля этой главы -3-a-voice/07-the-proof-moves-in/.