← Оглавление

1.11 Испытание

Сделать и проверить - не одно и то же. Мастер, только что закончивший изделие, знает одно: оно выглядит готовым. Правда ли оно готово - другой вопрос, и ответ на него не видно глазами. Задача умеет создавать себя, завершаться и отвечать на вопрос о своём состоянии. Вера удобна: не требует инструментов и работает мгновенно. Цена удобства обнаруживается именно тогда, когда всё начинает идти не так.

Что нужно

make ci проверяет форму: форматирование без нареканий, clippy без предупреждений. О поведении он не спрашивает. Код, который выглядит правильно, может вести себя неправильно - застава этого не заметит.

Нужен способ задавать вопросы о поведении и требовать ответа при каждом запуске.

Сборка

Тесты живут в специальном блоке в конце файла:

#[cfg(test)]
mod tests {
    use super::*;
}

#[cfg(test)] - атрибут конфигурации: этот блок компилируется только при cargo test. В обычную сборку он не попадает. use super::* вводит типы из родительского файла в область видимости тестов - без него Task и Status внутри блока неизвестны.

Тест - обычная функция с атрибутом #[test]. Имя описывает проверяемое поведение, а не метод:

#[test]
fn new_task_starts_as_todo() {
    let task = Task::new(1, "Buy coffee");
    assert!(!task.is_done());
}

assert! проверяет условие: если оно ложно - тест падает. Добавим второй тест:

#[test]
fn complete_marks_task_done() {
    let mut task = Task::new(1, "Buy coffee");
    task.complete();
    assert!(task.is_done());
}

Когда нужно сравнить два значения, assert_eq! покажет оба при падении:

#[test]
fn task_id_is_preserved() {
    let task = Task::new(42, "Buy coffee");
    assert_eq!(task.id, 42);
}

assert_eq!(left, right) - если left != right, тест падает и печатает оба значения. assert! этого не делает: он знает только “ложь”.

Итог

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn new_task_starts_as_todo() {
        let task = Task::new(1, "Buy coffee");
        assert!(!task.is_done());
    }

    #[test]
    fn complete_marks_task_done() {
        let mut task = Task::new(1, "Buy coffee");
        task.complete();
        assert!(task.is_done());
    }

    #[test]
    fn task_id_is_preserved() {
        let task = Task::new(42, "Buy coffee");
        assert_eq!(task.id, 42);
    }
}
running 3 tests
test tests::complete_marks_task_done ... ok
test tests::new_task_starts_as_todo ... ok
test tests::task_id_is_preserved ... ok

test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

make ci проходит. Застава теперь спрашивает не только о форме.

Полный код tq для этой главы - 1-a-task/11-the-first-proof/.


Lore: что не тестируется

Тесты проверяют поведение через возвращаемые значения и состояние. list возвращает () и только печатает - чтобы проверить вывод, нужно перехватывать stdout. Это возможно, но инструменты для этого появятся позже.