Post

[RUST] 러스트 프로그래밍 공식 가이드(제2판) 11장 요약

자동화 테스트 작성하기

테스트 작성 방법

  • 테스트란, 테스트 할 코드가 의도대로 기능하는지 검증하는 함수를 말하며, 보통 아래 3가지 동작을 수행함
    • 필요한 데이터나 상태 설정
    • 테스트할 코드 실행
    • 의도한 결과가 나오는지 확인

테스트 함수 파헤치기

  • 러스트에서 테스트란 test 속성(러스트 코드 조각에 대한 메타데이터를 의미)이 애너테이션된 함수를 말하며 fn 이전 줄에 #[test]를 추가하면 테스트 함수로 변경 됨
  • 테스트는 cargo test 명령어로 실행되며, 이 명령어를 실행하면 러스트는 속성이 표시된 함수를 실행하고 결과를 보고하는 테스트 실행 바이너리를 빌드함
  • 카고로 새 라이브러리 프로젝트를 생성할 때 마다 테스트 함수가 포함된 테스트 모듈이 자동으로 생성됨 (테스트 작성을 위한 템플릿을 제공)

ssert! 매크로로 결과 검사하기

  • 어떤 조건이 true 임을 보장하는 테스트를 작성할 땐 표준라이브러리가 제공하는 assert! 매크로가 유용
  • assert! 매크로는 불리언값으로 평가되는 인수를 전달받고, true 값일 경우 아무일도 일어나지 않고 테스트는 통과하고 false 값일 경우 panic! 매크로를 호출하여 테스트를 실패하도록 만듦

assert_eq!, assert_ne! 매크로를 이용한 동등 테스트

  • 두 매크로는 각각 동등한지(equanlity) 그렇지 않은지(inequality) 판단하고, 단언 코드가 실패하면 두 값을 출력하여 테스트 실패 사유를 더 알기 쉽게 보여줌
  • 러스트의 단언 함수의 매개변수는 left, right 라고 지칭하며 예상값과 테스트 코드로 만들어진 값의 순서는 상관이 없음
  • assert_eq! 매크로는 내부적으로 == 연산자를 사용 (같으면 통과)
  • assert_ne! 매크로는 내부적으로 != 연산자를 사용 (같지 않으면 통과)하며 적어도 이 값은 되지 않아야 함을 알고 있는 경우에 유용
  • 두 매크로는 비교 연산자를 사용하므로 비교 대상 타입은 반드시 PartialEq 트레이트를 구현해야 하며, 던언에 실패시 {:?} 디버그 포맷으로 두 값 (left, right)을 출력하므로 Debug 트레이트도 구현해야 함

커스텀 실패 메시지 추가하기

  • assert!, assert_eq!, assert_ne! 매크로에 필수적인 인수들 이후의 인수는 format! 매크로로 전달 되기 때문에 {} 자리표시자가 들어있는 포맷 문자열과 자리표시자에 들어갈 값을 전달할 수 있음
  • 커스텀 메시지는 테스트 단언의 의미를 서술하는데 유용

should_panic 매크로로 패닉 발생 검사하기

  • #[should_panic] 속성은 테스트 내부에서 패닉이 발생해야 통과되고, 패닉이 발생하지 않으면 실패
  • should_panic을 사용하는 테스트는 의도한 것과 다른 이유로 패닉이 발생하더라도 테스트가 통과하기 때문에 정확하지 않은데, 이때 should_panic의 속성에 expected 매개변수를 추가해 포함되어야 하는 메시지를 지정할 수 있음
    • 매개변수 값이 패닉 메시지 문자열의 일부라면 테스트는 통과
    • 매개변수 값이 패닉 메시지 문자열에 포함되지 않는다면 테스트는 실패

Result<T,E>를 이용한 테스트

  • 테스트는 Result<T,E>타입을 반환할 수 있으며, 테스트 성공시에는 Ok()를 실패 시에는 Err()를 반환할 수 있고 #[should_panic] 애너테이션을 사용할 수 없음
  • Err 배리언트를 반환하는 것을 단언하려면 Result<T, E>에 ? 연산자를 사용하지 않고 assert!(value.is_error())를 사용

테스트 실행 방법 제어하기

  • cargo test 는 커맨드 라인 옵션을 지정하여 기본 동작을 변경할 수 있음
  • 명령어 옵션은 cargo test에 전달되는 것도 있고, 테스트 바이너리에 전달되는 것도 있으며 이 둘을 구분하기 위해 cargo test에 전달할 인수를 먼저 나열하고, – 구분자를 쓰고, 그 뒤에 테스트 바이너리에게 전달할 인수를 나열

테스트를 병렬 혹은 순차적으로 실행하기

  • 여러 테스트를 실행할 때는 기본적으로 스레드를 사용해 병렬로 실행됨 (여러 테스트가 동시에 실행되므로, 각 테스트가 공유상태(공유자원, 현재 작업 디렉터리, 환경 변수 등)를 갖거나 다른 테스트에 의존해서는 안됨)
  • 테스트를 병렬로 실행하고 싶지 않거나, 사용할 스레드의 개수에 대해 미세 조정이 필요한 경우 --test-threads플래그와 함께 테스트 바이너리에서 사용할 스레드 개수를 지정 (cargo test -- --test-threads=1)

함수 출력 표시하기

  • 기본적으로 러스트 테스트 라이브러리는 성공한 테스트의 모든 표준 출력을 캡처함 (캡처: 즉시 터미널에 보여주는 게 아니라 일단 내부적으로 가로채서 저장)
  • 테스트에서 println! 매크로를 호출해도, 해당 테스트가 성공하면 터미널에서 println!출력을 찾아볼 수 없음 (테스트 실패 시 표준 출력으로 출력됐던 모든 내용이 실패 메시지 아래 표시)
  • 성공한 테스트에서 출력한 내용도 보고 싶다면, 러스트에게 —show-output 옵션을 전달하여 성공한 테스트 출력도 표시할 수 있음 (cargo test — —show-output)

이름을 지정해 일부 테스트만 실행하기

테스트 하나만 실행하기

  • cargo test 명령어에 테스트 함수의 이름을 인수로 넘겨 어떤 테스트를 실행할지 선택 (cargo test one_hundred)
  • 이름이 맞지 않는 테스트는 필터링되며 테스트 결과 마지막 요약 라인에 2 filtered out와 같이 표시하고, 실행한 테스트 이외에도 다른 테스트가 존재함을 알려줌

테스트를 필터링하여 어러 테스트 실행하기

  • 테스트 이름의 일부만 지정하면 해당 값에 맞는 모든 테스트가 실행 (cargo test add를 실행하면 함수명에 add 가 포함된 테스트가 실행됨)
  • 테스트가 위치한 모듈도 테스트 이름의 일부로 나타나며, 모듈 이름으로 필터링하면 해당 모듈 내 모든 테스트를 실행함

특별 요청이 없다면 일부 테스트 무시하기

  • 제외하고싶은 테스트가 있다면 ignore 속성(#[ignore])을 애너테이션 하여 테스트에서 제외 시킬 수 있음
  • cargo test -- --ignored 명령어를 사용하면 무시된 테스트만 실행할 수 있음
  • 무시되었건 말건 모든 테스트를 실행하고 싶다면 cargo test -- --include-ignored를 실행 할 수 있음

테스트 조직화

  • 유닛 테스트 : 한 번에 하나의 모듈만 테스트하며, 모듈의 비공개 인터페이스도 테스트할 수 있음
  • 통합 테스트 : 완전히 라이브러리 외부에 위치하며, 직접 작성한 라이브러리를 외부 코드에서 사용할 때와 똑같은 방식으로 사용하여 여러 모듀을 실제 사용 방식에 가깝게 결합해 전체 동작이 의도대로 동작하는지 검증하는 테스트

유닛 테스트

  • 유닛 테스트의 목적 : 각 코드 단위를 나머지 코드와 분리하여, 제대로 작동하지 않는 코드가 어느 부분인지 빠르게 파악하는 것
  • src 디렉터리 내의 각 파일에 테스트 대상이 될 코드와 함께 작성하며, 각 파일에 tests 모듈을 만들고 cfg(test)를 애너테이션하는 게 일반적인 관례

테스트 모듈과 #[cfg(test)]

  • 테스트 모듈에 애너테이션하는 #[cfg(test)]은 이 코드가 cargo test 명령어 실행 시에만 컴파일 및 실행 될 것이라는 점을 러스트에 전달 (cfg속성을 사용하면 카고는 cargo test 명령어를 실행할 때만 테스트 코드를 컴파일 함)
  • 유닛 테스트는 일반 코드와 같은 파일에 위치하기 때문에 #[cfg(test)] 애너테이션을 작성해 컴파일 결과물에 포함되지 않도록 명시해야 함

비공개 함수 테스트하기

  • 러스트의 비공개 규칙은 비공개 함수를 테스트하도록 허용 함

통합 테스트

  • 통합 테스트의 목적 : 라이브러리의 여러 부분을 함께 사용했을 때 제대로 작동하는지 확인하는 것

tests 디렉터리

  • 프로젝트 디렉터리의 아래 tests 디렉터리를 생성하면 카고는 디렉터리 내 통합 테스트 파일을 자동으로 인식 함
  • 원하는 만큼 통합 테스트 파일을 만들 수 있고, 카고는 각 파일을 개별 크레이트로 컴파일 함
  • cargo test 명령어에 테스트 함수명을 인수로 전달해 특정 통합 테스트 함수를 실행할 수 있음
  • 특정 통합 테스트 파일의 모든 테스트를 실행하려면 cargo test 명령어에 --test 인수로 파일명을 전달

통합 테스트 내 서브 모듈

  • tests 디렉터리 안의 파일들은 각각 독립된 크레이트로 컴파일하는데, tests/common.rs 와 같이 사용하는 대신 tests/common/mod.rs 처럼 사용하면 통합 테스트 크레이트로 취급되지 않고 일반 모듈로 동작함

바이너르 크레이트에서의 통합 테스트

  • 바이너리 크레이트는 tests 디렉터리에 통합 테스트를 만들어서 src/main.rs 파일에 정의된 함수를 use 구문으로 가져올 수 없음 (다른 크레이트에서 가져다 쓸 수 있게 노출하는 건 라이브러리 크레이트 뿐)
  • 주요 기능을 lib.rs 로 옮겨 통합 테스트에서 use 구문으로 가져와 테스트하고 main.rs 는 실행용으로만 사용
This post is licensed under CC BY 4.0 by the author.