테스트
우리 모두 알다시피 테스트는 모든 소프트웨어의 필수적인 부분입니다! Rust는 유닛 테스트와 통합 테스트를 위한 일류(first-class) 지원을 제공합니다(TRPL의 이 장을 참조하세요).
위에 링크된 테스트 장들에서 유닛 테스트와 통합 테스트를 작성하는 방법을 확인할 수 있습니다. 구조적으로 유닛 테스트는 테스트 대상 모듈에 배치하고, 통합 테스트는 별도의 tests/ 디렉터리에 배치할 수 있습니다.
foo
├── Cargo.toml
├── src
│ └── main.rs
│ └── lib.rs
└── tests
├── my_test.rs
└── my_other_test.rs
tests 내의 각 파일은 별도의 통합 테스트입니다. 즉, 의존성 있는 크레이트에서 호출되는 것처럼 라이브러리를 테스트하기 위한 것입니다.
테스트 장에서는 유닛(Unit), 문서(Doc), 통합(Integration)의 세 가지 테스트 스타일에 대해 자세히 설명합니다.
cargo는 당연히 모든 테스트를 실행할 수 있는 쉬운 방법을 제공합니다!
$ cargo test
다음과 같은 출력을 볼 수 있을 것입니다:
$ cargo test
Compiling blah v0.1.0 (file:///nobackup/blah)
Finished dev [unoptimized + debuginfo] target(s) in 0.89 secs
Running target/debug/deps/blah-d3b32b97275ec472
running 4 tests
test test_bar ... ok
test test_baz ... ok
test test_foo_bar ... ok
test test_foo ... ok
test result: ok. 4 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
``````shell
$ cargo test
Compiling blah v0.1.0 (file:///nobackup/blah)
Finished dev [unoptimized + debuginfo] target(s) in 0.89 secs
Running target/debug/deps/blah-d3b32b97275ec472
running 4 tests
test test_bar ... ok
test test_baz ... ok
test test_foo_bar ... ok
test test_foo ... ok
test result: ok. 4 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
이름이 패턴과 일치하는 테스트만 실행할 수도 있습니다:
$ cargo test test_foo
$ cargo test test_foo
Compiling blah v0.1.0 (file:///nobackup/blah)
Finished dev [unoptimized + debuginfo] target(s) in 0.35 secs
Running target/debug/deps/blah-d3b32b97275ec472
running 2 tests
test test_foo ... ok
test test_foo_bar ... ok
test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 2 filtered out
한 가지 주의할 점: Cargo는 여러 테스트를 동시에 실행할 수 있으므로 서로 경쟁(race)하지 않도록 확인해야 합니다.
이러한 동시성이 문제를 일으키는 한 가지 예는 아래와 같이 두 테스트가 하나의 파일에 출력하는 경우입니다:
#![allow(unused)]
fn main() {
#[cfg(test)]
mod tests {
// 필요한 모듈 가져오기
use std::fs::OpenOptions;
use std::io::Write;
// 이 테스트는 파일에 씁니다
#[test]
fn test_file() {
// ferris.txt 파일을 열거나 없으면 생성합니다.
let mut file = OpenOptions::new()
.append(true)
.create(true)
.open("ferris.txt")
.expect("ferris.txt를 여는 데 실패했습니다");
// "Ferris"를 5번 출력합니다.
for _ in 0..5 {
file.write_all("Ferris\n".as_bytes())
.expect("ferris.txt에 쓸 수 없습니다");
}
}
// 이 테스트는 동일한 파일에 쓰기를 시도합니다
#[test]
fn test_file_also() {
// ferris.txt 파일을 열거나 없으면 생성합니다.
let mut file = OpenOptions::new()
.append(true)
.create(true)
.open("ferris.txt")
.expect("ferris.txt를 여는 데 실패했습니다");
// "Corro"를 5번 출력합니다.
for _ in 0..5 {
file.write_all("Corro\n".as_bytes())
.expect("ferris.txt에 쓸 수 없습니다");
}
}
}
}
의도는 다음과 같은 결과를 얻는 것이었지만:
$ cat ferris.txt
Ferris
Ferris
Ferris
Ferris
Ferris
Corro
Corro
Corro
Corro
Corro
ferris.txt에 실제로 기록된 내용은 다음과 같습니다:
$ cargo test test_file && cat ferris.txt
Corro
Ferris
Corro
Ferris
Corro
Ferris
Corro
Ferris
Corro
Ferris