프로그램 코드 테스트는 개발 산업에서 가장 오래되고 중요한 프로세스 중 하나입니다. 그러나 많은 언어에서 테스트 라이브러리는 별도로 제공되며, 테스트를 주요 코드에 통합하는 것은 불투명하거나 불편할 수 있습니다. 러스트는 초기 버전부터 명시적인 모듈 테스트 지원을 내장하여 설계되었습니다.
많은 전통적인 언어에서 테스트는 별도로 위치하거나 특정 프레임워크 및 빌더를 설정해야 합니다. 이는 공동 작업을 복잡하게 하고, 기본 로직과 테스트 간의 비동기화 위험을 증가시키며, 통합 및 모듈 테스트 수행을 복잡하게 만듭니다.
러스트는 언어 수준에서 테스트 시스템을 통합합니다. #[cfg(test)] 모듈, #[test] 지시문 및 cargo test 도구를 통해 소스 코드 내에서 테스트를 작성, 실행 및 관리할 수 있습니다. 이는 코드와 그것을 검증하는 테스트 간의 긴밀한 연결을 보장하며, 테스트 실행의 용이성과 CI/CD 프로세스의 자동화를 보장합니다.
코드 예시:
// tests automatically compiled and run by `cargo test` #[cfg(test)] mod tests { use super::*; #[test] fn it_adds_two() { assert_eq!(2 + 2, 4); } }
주요 특징들:
cargo test)로 발견된 모든 테스트를 실행함.#[cfg(test)] 사용을 통해 테스트를 릴리스 빌드에 포함하지 않음.테스트에서 불안정한 또는 비공식적인 기능을 사용할 수 있습니까? 어떻게 해야 합니까?
네, 가능합니다. #[cfg(test)]로 선언된 테스트 모듈 내에서는 테스트가 현재 모듈의 공개 및 비공식 API를 모두 볼 수 있습니다. 이는 구현 세부 사항을 직접 테스트할 수 있게 해줍니다. 그러나 다른 모듈에서는 오직 공개 인터페이스를 통해서만 가능합니다.
예시:
fn private_internal(x: i32) -> i32 { x + 1 } #[cfg(test)] mod tests { use super::*; #[test] fn test_private() { assert_eq!(private_internal(41), 42); } }
같은 이름의 테스트가 서로 다른 모듈에서 충돌할까요?
아니요, 테스트 이름은 자신의 모듈 내에서만 보입니다. 서로 다른 모듈의 테스트는 같은 이름을 가질 수 있으며, 컴파일러는 전체 경로(namespace)를 통해 이를 구분합니다.
예시:
mod a { #[cfg(test)] mod tests { #[test] fn basic() {} } } mod b { #[cfg(test)] mod tests { #[test] fn basic() {} } }
개별 테스트 또는 테스트 세트의 일부를 실행할 수 있습니까?
네, 테스트 이름 필터링을 통해 실행할 수 있습니다: cargo test 테스트이름. 이는 대규모 세트 디버깅에 유용합니다.
예시:
cargo test only_this_test
개발자가 테스트를 별도 프로젝트로 이동하여 비공식 기능에 대한 접근 없이 다른 디렉터리 구조를 사용했습니다. 주 라이브러리의 변경으로 인해 테스트가 빠르게 뒤처지고 비효율적으로 되었습니다.
장점: 테스트를 격리시켜 테스트 모듈이 릴리스 빌드에 포함되는 것을 피할 수 있습니다.
단점: 비동기화 손실, 내부 세부 사항을 테스트 할 수 없음, 높은 유지 관리 및 테스트의 비효율성 위험이 발생합니다.
테스트가 기본 로직과 동일한 모듈 내에 직접 작성되며, 작은 비원자적 테스트 케이스가 비공식 및 공개 API를 테스트합니다. 초보자가 테스트를 통해 모듈 작업에 빠르게 익숙해집니다.
장점: 최대의 동기화 지원, 투명성, 빠른 로컬 테스트, 테스트 주도 개발에 적합함.
단점: 소스 파일 내 코드 줄 수 증가, 테스트 수 증가 시 복잡성 증가할 가능성.