소개
테스트 실행 제어 방법에 오신 것을 환영합니다. 이 랩은 Rust Book의 일부입니다. LabEx 에서 Rust 기술을 연습할 수 있습니다.
이 랩에서는 cargo test에 대한 명령줄 옵션과 결과 테스트 바이너리를 사용하여 Rust 에서 테스트 실행 동작을 제어하는 방법을 배우게 됩니다.
테스트 실행 제어 방법에 오신 것을 환영합니다. 이 랩은 Rust Book의 일부입니다. LabEx 에서 Rust 기술을 연습할 수 있습니다.
이 랩에서는 cargo test에 대한 명령줄 옵션과 결과 테스트 바이너리를 사용하여 Rust 에서 테스트 실행 동작을 제어하는 방법을 배우게 됩니다.
cargo run이 코드를 컴파일한 다음 결과 바이너리를 실행하는 것처럼, cargo test는 코드를 테스트 모드로 컴파일하고 결과 테스트 바이너리를 실행합니다. cargo test에 의해 생성된 바이너리의 기본 동작은 모든 테스트를 병렬로 실행하고 테스트 실행 중에 생성된 출력을 캡처하여 출력이 표시되지 않도록 하고 테스트 결과와 관련된 출력을 더 쉽게 읽을 수 있도록 하는 것입니다. 그러나 명령줄 옵션을 지정하여 이 기본 동작을 변경할 수 있습니다.
일부 명령줄 옵션은 cargo test로 전달되고, 일부는 결과 테스트 바이너리로 전달됩니다. 이 두 가지 유형의 인수를 구분하기 위해, cargo test로 전달되는 인수를 나열한 다음 구분자 --를 사용하고, 그 다음에 테스트 바이너리로 전달되는 인수를 나열합니다. cargo test --help를 실행하면 cargo test와 함께 사용할 수 있는 옵션이 표시되고, cargo test -- --help를 실행하면 구분자 뒤에 사용할 수 있는 옵션이 표시됩니다.
여러 테스트를 실행할 때, 기본적으로 스레드를 사용하여 병렬로 실행되므로 더 빠르게 완료되고 더 빠르게 피드백을 받습니다. 테스트가 동시에 실행되므로, 테스트가 서로 또는 현재 작업 디렉토리나 환경 변수와 같은 공유 환경을 포함한 공유 상태에 의존하지 않도록 해야 합니다.
예를 들어, 각 테스트가 test-output.txt라는 이름의 디스크에 파일을 생성하고 해당 파일에 일부 데이터를 쓰는 코드를 실행한다고 가정해 보겠습니다. 그런 다음 각 테스트는 해당 파일의 데이터를 읽고 파일에 특정 값이 포함되어 있는지 확인하며, 각 테스트마다 값이 다릅니다. 테스트가 동시에 실행되므로, 한 테스트가 다른 테스트가 파일을 쓰고 읽는 시간 사이에 파일을 덮어쓸 수 있습니다. 그러면 두 번째 테스트가 실패하게 되는데, 이는 코드가 잘못되었기 때문이 아니라 테스트가 병렬로 실행되는 동안 서로 간섭했기 때문입니다. 한 가지 해결책은 각 테스트가 다른 파일에 쓰도록 하는 것이고, 다른 해결책은 테스트를 한 번에 하나씩 실행하는 것입니다.
테스트를 병렬로 실행하고 싶지 않거나 사용되는 스레드 수에 대해 더 세밀한 제어를 원한다면, --test-threads 플래그와 사용하려는 스레드 수를 테스트 바이너리로 보낼 수 있습니다. 다음 예제를 살펴보세요.
cargo test -- --test-threads=1
테스트 스레드 수를 1로 설정하여 프로그램이 병렬 처리를 사용하지 않도록 했습니다. 하나의 스레드를 사용하여 테스트를 실행하는 것은 병렬로 실행하는 것보다 시간이 더 오래 걸리지만, 테스트가 상태를 공유하는 경우 서로 간섭하지 않습니다.
기본적으로, 테스트가 통과하면 Rust 의 테스트 라이브러리는 표준 출력에 인쇄된 모든 것을 캡처합니다. 예를 들어, 테스트에서 println!을 호출하고 테스트가 통과하면 터미널에서 println! 출력을 볼 수 없습니다. 테스트가 통과했음을 나타내는 줄만 보게 됩니다. 테스트가 실패하면 실패 메시지의 나머지 부분과 함께 표준 출력에 인쇄된 모든 것을 보게 됩니다.
예를 들어, Listing 11-10 에는 매개변수 값을 인쇄하고 10 을 반환하는 어리석은 함수와 통과하는 테스트 및 실패하는 테스트가 있습니다.
Filename: src/lib.rs
fn prints_and_returns_10(a: i32) -> i32 {
println!("I got the value {a}");
10
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn this_test_will_pass() {
let value = prints_and_returns_10(4);
assert_eq!(10, value);
}
#[test]
fn this_test_will_fail() {
let value = prints_and_returns_10(8);
assert_eq!(5, value);
}
}
Listing 11-10: println!을 호출하는 함수에 대한 테스트
cargo test로 이러한 테스트를 실행하면 다음과 같은 출력이 표시됩니다.
running 2 tests
test tests::this_test_will_pass ... ok
test tests::this_test_will_fail ... FAILED
failures:
---- tests::this_test_will_fail stdout ----
1 I got the value 8
thread 'main' panicked at 'assertion failed: `(left == right)`
left: `5`,
right: `10`', src/lib.rs:19:9
note: run with `RUST_BACKTRACE=1` environment variable to display
a backtrace
failures:
tests::this_test_will_fail
test result: FAILED. 1 passed; 1 failed; 0 ignored; 0 measured; 0
filtered out; finished in 0.00s
이 출력 어디에서도 테스트가 통과할 때 인쇄되는 I got the value 4를 볼 수 없습니다. 해당 출력은 캡처되었습니다. 실패한 테스트의 출력인 I got the value 8 [1]은 테스트 요약 출력 섹션에 나타나며, 테스트 실패의 원인도 표시합니다.
통과하는 테스트의 인쇄된 값도 보려면 --show-output을 사용하여 성공적인 테스트의 출력도 표시하도록 Rust 에 지시할 수 있습니다.
cargo test -- --show-output
Listing 11-10 의 테스트를 --show-output 플래그로 다시 실행하면 다음과 같은 출력이 표시됩니다.
running 2 tests
test tests::this_test_will_pass ... ok
test tests::this_test_will_fail ... FAILED
successes:
---- tests::this_test_will_pass stdout ----
I got the value 4
successes:
tests::this_test_will_pass
failures:
---- tests::this_test_will_fail stdout ----
I got the value 8
thread 'main' panicked at 'assertion failed: `(left == right)`
left: `5`,
right: `10`', src/lib.rs:19:9
note: run with `RUST_BACKTRACE=1` environment variable to display
a backtrace
failures:
tests::this_test_will_fail
test result: FAILED. 1 passed; 1 failed; 0 ignored; 0 measured; 0
filtered out; finished in 0.00s
때로는 전체 테스트 스위트를 실행하는 데 시간이 오래 걸릴 수 있습니다. 특정 영역의 코드를 작업하는 경우 해당 코드와 관련된 테스트만 실행할 수 있습니다. 실행하려는 테스트의 이름 (들) 을 인수로 cargo test에 전달하여 실행할 테스트를 선택할 수 있습니다.
테스트 하위 집합을 실행하는 방법을 보여주기 위해 먼저 Listing 11-11 에 표시된 대로 add_two 함수에 대한 세 개의 테스트를 생성하고 실행할 테스트를 선택합니다.
Filename: src/lib.rs
pub fn add_two(a: i32) -> i32 {
a + 2
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn add_two_and_two() {
assert_eq!(4, add_two(2));
}
#[test]
fn add_three_and_two() {
assert_eq!(5, add_two(3));
}
#[test]
fn one_hundred() {
assert_eq!(102, add_two(100));
}
}
Listing 11-11: 세 개의 서로 다른 이름을 가진 세 개의 테스트
앞서 보았듯이 인수를 전달하지 않고 테스트를 실행하면 모든 테스트가 병렬로 실행됩니다.
running 3 tests
test tests::add_three_and_two ... ok
test tests::add_two_and_two ... ok
test tests::one_hundred ... ok
test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0
filtered out; finished in 0.00s
cargo test에 테스트 함수의 이름을 전달하여 해당 테스트만 실행할 수 있습니다.
[object Object]
one_hundred라는 이름의 테스트만 실행되었고, 다른 두 테스트는 해당 이름과 일치하지 않았습니다. 테스트 출력은 마지막에 2 filtered out을 표시하여 실행되지 않은 테스트가 더 있음을 알려줍니다.
이러한 방식으로 여러 테스트의 이름을 지정할 수 없습니다. cargo test에 제공된 첫 번째 값만 사용됩니다. 하지만 여러 테스트를 실행하는 방법이 있습니다.
테스트 이름의 일부를 지정할 수 있으며, 해당 값과 일치하는 이름을 가진 모든 테스트가 실행됩니다. 예를 들어, 테스트 이름 중 두 개에 add가 포함되어 있으므로 cargo test add를 실행하여 해당 두 테스트를 실행할 수 있습니다.
[object Object]
이 명령은 이름에 add가 포함된 모든 테스트를 실행하고 one_hundred라는 이름의 테스트를 필터링했습니다. 또한 테스트가 나타나는 모듈이 테스트 이름의 일부가 되므로, 모듈 이름을 기준으로 필터링하여 모듈의 모든 테스트를 실행할 수 있습니다.
때로는 특정 테스트 몇 개를 실행하는 데 시간이 오래 걸릴 수 있으므로, cargo test의 대부분의 실행에서 해당 테스트를 제외하고 싶을 수 있습니다. 실행하려는 모든 테스트를 인수로 나열하는 대신, 다음 예제와 같이 ignore 속성을 사용하여 시간이 오래 걸리는 테스트에 주석을 달아 제외할 수 있습니다.
파일 이름: src/lib.rs
#[test]
fn it_works() {
let result = 2 + 2;
assert_eq!(result, 4);
}
#[test]
#[ignore]
fn expensive_test() {
// code that takes an hour to run
}
#[test] 뒤에, 제외하려는 테스트에 #[ignore] 줄을 추가합니다. 이제 테스트를 실행하면 it_works가 실행되지만 expensive_test는 실행되지 않습니다.
[object Object]
expensive_test 함수는 ignored로 나열됩니다. 무시된 테스트만 실행하려면 cargo test -- --ignored를 사용할 수 있습니다.
[object Object]
어떤 테스트를 실행할지 제어하여 cargo test 결과가 빠르게 반환되도록 할 수 있습니다. ignored 테스트의 결과를 확인하고 결과를 기다릴 시간이 있을 때, cargo test -- --ignored를 실행할 수 있습니다. 무시되었는지 여부에 관계없이 모든 테스트를 실행하려면 cargo test -- --include-ignored를 실행할 수 있습니다.
축하합니다! 테스트 실행 방법 제어 랩을 완료했습니다. LabEx 에서 더 많은 랩을 연습하여 기술을 향상시킬 수 있습니다.