자동화된 테스트 작성하기

자동화된 테스트 작성하기 (Writing Automated Tests)

프로그램이 제대로 동작하는지 확인하는 가장 확실한 방법 중 하나가 테스트예요. 이번 챕터에서는 Rust가 어떻게 자동화된 테스트 작성을 지원하는지, 그리고 테스트를 어떻게 구성하고 실행하는지를 차근차근 살펴볼게요.

출처: Rust 공식문서 — Writing Automated Tests

본문

Edsger W. Dijkstra는 1972년 에세이 “The Humble Programmer”에서 이렇게 말했어요. "프로그램 테스트는 버그의 존재를 보여주는 데는 매우 효과적이지만, 버그가 없다는 것을 보여주는 데는 절망적으로 부족하다." 그렇다고 해서 테스트를 최대한 많이 하지 않아도 된다는 뜻은 아니에요. 오히려 우리가 할 수 있는 만큼 열심히 테스트해야 하죠.

프로그램의 정확성(correctness)은 코드가 우리가 의도한 대로 동작하는 정도를 뜻해요. Rust는 프로그램의 정확성에 대해 깊이 신경 쓰도록 설계되었지만, 정확성이라는 건 복잡하고 증명하기도 쉽지 않아요. Rust의 타입 시스템이 이 부담의 상당 부분을 짊어지지만, 타입 시스템으로 모든 걸 잡아낼 수는 없어요. 그래서 Rust는 자동화된 소프트웨어 테스트를 작성하는 기능을 함께 제공해요.

예를 들어 전달받은 숫자에 2를 더해주는 함수 add_two를 작성한다고 해볼게요. 이 함수의 시그니처(signature)는 정수를 파라미터로 받고 정수를 결과로 반환해요. 우리가 이 함수를 구현하고 컴파일할 때, Rust는 지금까지 배운 모든 타입 검사와 borrow 검사를 수행해서 예컨대 이 함수에 String 값을 넘기거나 유효하지 않은 참조를 넘기지는 않는지 확인해줘요.

하지만 Rust는 이 함수가 우리가 정확히 의도한 대로 동작하는지, 즉 파라미터에 2를 더하는지 — 예를 들어 10을 더하거나 50을 빼는 게 아닌지 — 는 확인해주지 못해요. 바로 여기서 테스트가 필요해지는 거죠. 우리는 테스트를 작성해서, 예를 들어 add_two3을 전달했을 때 반환값이 5가 되는지를 검증(assert)할 수 있어요. 그리고 코드를 수정할 때마다 이 테스트를 실행해서 기존에 제대로 동작하던 동작이 바뀌지 않았는지 확인할 수 있어요.

테스트는 복잡한 기술이에요. 좋은 테스트를 쓰는 법의 모든 세부사항을 한 챕터에서 다룰 수는 없지만, 이 챕터에서는 Rust 테스트 기능의 작동 방식(mechanics)을 다룰 거예요. 테스트를 작성할 때 쓸 수 있는 어노테이션(annotation)과 매크로(macro), 테스트를 실행할 때 제공되는 기본 동작과 옵션, 그리고 테스트를 단위 테스트(unit test)와 통합 테스트(integration test)로 구성하는 방법까지 하나씩 이야기해볼게요.

더 알아보기