[2026] Rust 테스팅 | 단위 테스트, 통합 테스트, 벤치마크

[2026] Rust 테스팅 | 단위 테스트, 통합 테스트, 벤치마크

이 글의 핵심

cargo test로 단위·통합 테스트를 바로 돌릴 수 있어, CI에 붙이기도 좋습니다. #[test]와 #[should_panic] 등으로 실패·패닉을 명시적으로 검증할 수 있습니다.

들어가며

cargo test로 단위·통합 테스트를 바로 돌릴 수 있어, CI에 붙이기도 좋습니다. #[test]#[should_panic] 등으로 실패·패닉을 명시적으로 검증할 수 있습니다. 단위 테스트는 모든 언어에서 중요합니다. Python에서 pytest·CI, Node.js의 Jest, C++의 Google Test, Go의 go test는 각각의 생태계에서 표준에 가깝습니다. CI/CD·컨테이너 배포와 연결하려면 Node.js GitHub Actions CI/CDC++ Docker·배포 이미지를 함께 보세요.

1. 단위 테스트 (Unit Tests)

기본 테스트

다음은 rust를 활용한 상세한 구현 코드입니다. 함수를 통해 로직을 구현합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

fn add(a: i32, b: i32) -> i32 {
    a + b
}
fn subtract(a: i32, b: i32) -> i32 {
    a - b
}
#[cfg(test)]
mod tests {
    use super::*;
    
    #[test]
    fn test_add() {
        assert_eq!(add(2, 3), 5);
        assert_eq!(add(-2, 3), 1);
        assert_eq!(add(0, 0), 0);
    }
    
    #[test]
    fn test_subtract() {
        assert_eq!(subtract(10, 3), 7);
        assert_eq!(subtract(5, 10), -5);
    }
}

테스트 실행

아래 코드는 bash를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

# 모든 테스트 실행
cargo test
# 특정 테스트만 실행
cargo test test_add
# 출력 보기
cargo test -- --nocapture
# 단일 스레드로 실행
cargo test -- --test-threads=1

2. assert 매크로

기본 assert

다음은 rust를 활용한 상세한 구현 코드입니다. 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

#[cfg(test)]
mod tests {
    #[test]
    fn test_assertions() {
        // assert!: 조건이 true인지 확인
        assert!(true);
        assert!(2 + 2 == 4);
        
        // assert_eq!: 두 값이 같은지
        assert_eq!(2 + 2, 4);
        assert_eq!("hello".to_uppercase(), "HELLO");
        
        // assert_ne!: 두 값이 다른지
        assert_ne!(2 + 2, 5);
    }
    
    #[test]
    fn test_with_message() {
        let x = 10;
        assert!(x > 5, "x는 5보다 커야 함, 실제: {}", x);
        assert_eq!(x, 10, "x는 10이어야 함");
    }
}

should_panic

다음은 rust를 활용한 상세한 구현 코드입니다. 함수를 통해 로직을 구현합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

fn divide(a: i32, b: i32) -> i32 {
    if b == 0 {
        panic!("0으로 나눌 수 없음");
    }
    a / b
}
#[cfg(test)]
mod tests {
    use super::*;
    
    #[test]
    #[should_panic]
    fn test_divide_by_zero() {
        divide(10, 0);
    }
    
    #[test]
    #[should_panic(expected = "0으로 나눌 수 없음")]
    fn test_divide_panic_message() {
        divide(10, 0);
    }
}

Result를 반환하는 테스트

아래 코드는 rust를 사용한 구현 예제입니다. 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

#[cfg(test)]
mod tests {
    #[test]
    fn test_with_result() -> Result<(), String> {
        if 2 + 2 == 4 {
            Ok(())
        } else {
            Err(String::from("계산 오류"))
        }
    }
}

3. 통합 테스트 (Integration Tests)

tests/ 디렉토리

아래 코드는 rust를 사용한 구현 예제입니다. 함수를 통해 로직을 구현합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// tests/integration_test.rs
use my_crate;
#[test]
fn test_add() {
    let result = my_crate::add(2, 3);
    assert_eq!(result, 5);
}
#[test]
fn test_subtract() {
    let result = my_crate::subtract(10, 3);
    assert_eq!(result, 7);
}

공통 모듈

아래 코드는 rust를 사용한 구현 예제입니다. 함수를 통해 로직을 구현합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// tests/common/mod.rs
pub fn setup() {
    // 테스트 초기화 코드
}
// tests/integration_test.rs
mod common;
#[test]
fn test_with_setup() {
    common::setup();
    // 테스트 코드
}

4. 실전 예제

예제: 계산기 테스트

다음은 rust를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

struct Calculator;
impl Calculator {
    fn add(a: i32, b: i32) -> i32 {
        a + b
    }
    
    fn divide(a: i32, b: i32) -> Result<i32, String> {
        if b == 0 {
            Err(String::from("0으로 나눌 수 없음"))
        } else {
            Ok(a / b)
        }
    }
}
#[cfg(test)]
mod tests {
    use super::*;
    
    #[test]
    fn test_add() {
        assert_eq!(Calculator::add(2, 3), 5);
        assert_eq!(Calculator::add(-2, 3), 1);
    }
    
    #[test]
    fn test_divide_success() {
        assert_eq!(Calculator::divide(10, 2), Ok(5));
    }
    
    #[test]
    fn test_divide_by_zero() {
        assert!(Calculator::divide(10, 0).is_err());
    }
}

실전 심화 보강

실전 예제: 임시 디렉터리와 assert_fs 스타일 통합 테스트

표준 라이브러리만으로 파일 시스템을 건드리는 함수를 검증하는 최소 예제입니다. tempfile 크레이트를 쓰면 더 간결합니다. Cargo.toml (dev-dependencies):

[dev-dependencies]
tempfile = "3"

다음은 rust를 활용한 상세한 구현 코드입니다. 함수를 통해 로직을 구현합니다, 반복문으로 데이터를 처리합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

use std::fs;
use std::path::Path;
fn merge_logs(dir: &Path) -> std::io::Result<String> {
    let mut out = String::new();
    let mut entries: Vec<_> = fs::read_dir(dir)?
        .filter_map(|e| e.ok())
        .map(|e| e.path())
        .filter(|p| p.extension().map(|x| x == "log").unwrap_or(false))
        .collect();
    entries.sort();
    for p in entries {
        out.push_str(&fs::read_to_string(&p)?);
    }
    Ok(out)
}
#[cfg(test)]
mod tests {
    use super::*;
    use std::io::Write;
    use tempfile::tempdir;
    #[test]
    fn orders_files_lexicographically() -> std::io::Result<()> {
        let dir = tempdir()?;
        let a = dir.path().join("b.log");
        let b = dir.path().join("a.log");
        fs::write(&a, "second\n")?;
        fs::write(&b, "first\n")?;
        let merged = merge_logs(dir.path())?;
        assert_eq!(merged, "first\nsecond\n");
        Ok(())
    }
}

자주 하는 실수

  • 전역 상태(환경 변수, 현재 디렉터리)에 의존한 테스트가 순서에 따라 실패하는 경우.
  • #[should_panic]만 걸고 메시지를 검증하지 않아 잘못된 panic에도 통과하는 경우.
  • 통합 테스트에서 use crate::가 아니라 크레이트 이름으로만 import해야 하는 규칙을 혼동하는 경우.

주의사항

  • cargo test기본적으로 병렬입니다. 공유 자원이 있으면 Mutex 또는 --test-threads=1을 고려하세요.
  • 벤치마크criterion 등으로 분리해 릴리스 프로파일에서 측정합니다.

실무에서는 이렇게

  • 프로퍼티 기반 테스트(proptest)로 입력 공간을 넓혀 엣지 케이스를 잡습니다.
  • CI에서는 cargo test —lockedcargo clippy — -D warnings를 함께 돌립니다.

비교 및 대안

종류용도
단위 테스트순수 함수, 빠른 피드백
통합 테스트바이너리 경계, 파일·네트워크
문서 테스트예제 코드 동기화

추가 리소스


정리

핵심 요약

  1. #[test]: 테스트 함수 표시
  2. assert!: 조건 검증
  3. assert_eq!/assert_ne!: 값 비교
  4. #[should_panic]: panic 테스트
  5. 통합 테스트: tests/ 디렉토리

다음 단계


관련 글

... 996 lines not shown ... Token usage: 63706/1000000; 936294 remaining Start-Sleep -Seconds 3