테스트 함수
Rust에서 테스트는 함수로 구현되며, 테스트 코드를 넣기 위해서는 함수(fn)앞에 #[test] attribute를 붙이면 됩니다.
#[test]
fn add_test() {
assert!(1 + 1 == 2);
}
위와 같은 코드를 작성하고 cargo test를 실행하면 cargo는 #[test]가 붙어있는 함수를 찾아 차례로 실행하게 됩니다.
assert!()매크로 안의 식이 참이면 pass, 거짓이면 error로 취급합니다.
$ cargo test
running 1 test
test add_test ... ok
테스트 모듈
테스트모듈은 크게 Unit Test와 Intergration Test로 구분합니다.
Unit Test : 각 소스 단위를 테스트 합니다. src폴더 안의 각 소스 파일 안에 test라는 모듈로 작성합니다. 각 소스 파일에 있는 private/public을 사용합니다.
Intergration Test : 여러 코드를 통합하여 테스트 합니다. 별도의 파일에 작성합니다. 여러 소스의 public아이템을 사용합니다.
Unit Test
유닛 테스트 모듈은 아래 코드와 같이 mod 모듈명(보통 tests 로 명명하지만 다른 명칭도 가능합니다.) 앞에 #[cfg(test)] attribute를 붙이고, 그 안에 test 함수(들)을 갖습니다. 테스트 모듈 안에는 보통 여러 개의 테스트 함수들을 둘 수 있으며, 필요한 경우 여러 테스트 함수들에서 호출할 수 있는 보조함수들을 넣을 수 있습니다.
#[cfg(test)]
mod tests {
#[test]
fn add_test() {
assert!(1 + 1 == 2);
}
#[test]
fn mul_test() {
assert_eq!(2 * 3, 6);
}
}
#[cfg(test)] attribute는 "cargo test"를 실행할 때만 컴파일되고 실행되도록 지시하는 것으로 이 섹션은 "cargo build" 등과 같은 명령에서 컴파일되지 않습니다. 코드를 빌드(cargo build)할 때, 일반적으로 테스트 코드를 함께 넣을 필요가 없으며, 만약 넣게 되면 코드 사이즈가 불필요하게 커질 것이므로 이 부분을 제외하도록 일반적으로 #[cfg(test)] attribute를 사용합니다.
Integration Test
일반적으로 "tests" 라는 디렉토리(src 디렉토리와 동일한 레벨)에 넣게 되며, 테스트 파일 안에 각 테스트 함수를 직접 사용하거나 테스트 모듈을 만들어 사용합니다. 테스트 모듈의 경우 Unit Test와 달리 별도의 테스트 파일을 사용하기 때문에 테스트 모듈 위에 #[cfg(test)] attribute를 사용할 필요가 없습니다.
assert 매크로
assert!(식) 매크로는 "식"에 있는 값이 true이면 테스트가 성공한 것으로 여기고, false 이면 에러인 것으로 취급합니다.
assert!() 매크로는 하나의 표현식을 받아들여 그 결과가 true인 지 false인 지만 체크하는 반면, assert_eq!() 혹은 assert_ne!() 매크로는 2개의 파라미터를 받아들여 에러가 난 경우 두 파라미터의 값이 어떻게 다른지 출력해 줍니다. assert_eq!() 매크로는 둘이 같은 지를 체크하고 같지 않으면 에러를 발생시킵니다. 반대로 assert_ne!() 매크로는 둘이 같지 않으면 성공하고 같으면 에러를 발생시킵니다.
예를 들어, 아래 코드는 고의로 두번째 파라미터에 6 대신 5를 넣은 것으로 이 테스트는 에러가 발생한 것입니다.
#[cfg(test)]
mod tests {
#[test]
fn mul_test() {
assert_eq!(2 * 3, 5); // 에러
}
}
테스트가 성공하면 assert!() 매크로나 assert_eq!() 매크로는 아무런 차이가 없지만, assert_eq!() 매크로는 실패하면 아래와 같이 좌, 우 파라미터가 어떻게 다른지 자세하게 출력하게 됩니다. Rust의 assert 매크로는 (다른 test framework과 달리) expected vs actual 의 구분이 없이, 그냥 두 파라미터를 비교합니다.
test tests::mul_test ... FAILED
failures:
---- tests::mul_test stdout ----
thread 'tests::mul_test' panicked at 'assertion failed: `(left == right)`
left: `6`,
right: `5`', src\lib.rs:48:9
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
만약 assert 매크로에 사용자가 정의하는 에러 메시지를 넣기 위해서는 assert!() 매크로의 경우는 첫번째 파라미터 이후에, assert_eq!(), assert_ne!() 매크로의 경우는 두번째 파라미터 이후에 format!에 전달할 수 있는 값들을 넣으면 된다. 예를 들어, 아래는 assert!() 매크로에 커스텀 메시지를 넣은 것으로, "wrong. expecting: {}", 2 와 같이 2개의 파라미터를 넣고 있습니다. 이는 format!() 매크로 안에 들어가는 2개의 파라미터입니다.
#[test]
fn add_test() {
assert!(1 + 1 == 3, "wrong. expecting: {}", 2);
}
test tests::add_test ... FAILED
failures:
---- tests::add_test stdout ----
thread 'tests::add_test' panicked at 'wrong. expecting: 2', src\lib.rs:43:9
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
should_panic
만약 테스트가 panic! 이 호출될 것으로 예측한다면, 그 테스트 함수 위에 #[should_panic] attribute를 넣으면 됩니다. 이 attribute는 테스트가 실행되었을 때, panic이 나면 성공(pass)으로 취급하게 되고, panic이 나지 않으면 에러로 취급합니다.
아래 코드는 div() 함수에서 b=0 인 경우 panic! 이 발생하는데, 이를 테스트하기 위해 div_test() 테스트 함수 위에 #[should_panic] 을 넣은 것입니다.
pub fn div(a: i32, b: i32) -> i32 {
if b == 0 {
panic!("b cannot be zero");
}
a / b
}
#[cfg(test)]
mod tests {
#[test]
#[should_panic]
fn div_test() {
super::div(3, 0); // panic 발생
}
}
'Rust' 카테고리의 다른 글
Rust Trait (160) | 2023.06.20 |
---|---|
Rust Generic 데이터 타입 (91) | 2023.06.19 |
Rust 파이썬 바인딩 (56) | 2023.05.29 |
Rust 비동기 프로그래밍 (36) | 2023.05.28 |
Rust 멀티스레딩 (28) | 2023.05.27 |