단위 테스트 (Unit Testing)

Author

Natalia Andriychuk

Published

March 17, 2023

코드를 테스트하는 것은 번거로울 수 있지만, 코드의 품질을 높이고 예기치 않은 버그를 방지하는 가장 확실한 방법입니다. R의 대표적인 테스트 패키지인 {testthat}을 사용하는 법을 배웁니다.

목표

  • 자동화된 테스트의 이점 이해
  • {testthat} 패키지의 기본 사용법 익히기
  • 기대치(Expectation) 함수 활용하기
  • 테스트 가능한 코드 작성하기

1. 테스트의 이점

우리는 보통 함수를 만든 뒤 콘솔에서 몇 가지 값을 넣어보며 눈으로 결과를 확인합니다. 하지만 시간이 흐른 뒤 코드를 수정했을 때, 예전에 잘 작동하던 기능이 여전히 잘 돌아가는지 매번 수동으로 확인하는 것은 불가능에 가깝습니다.

자동화된 단위 테스트(Automated Unit Testing)를 사용하면: - 코드 변경 시 기존 기능이 망가졌는지 즉시 확인 가능합니다. - 에러가 발생한 위치를 더 빨리 찾을 수 있습니다. - 테스트 코드 자체가 훌륭한 문서 역할을 합니다. - 테스트하기 쉬운 코드를 짜게 되므로 전체적인 설계가 개선됩니다.

2. {testthat}의 기대치(Expectation)

기대치는 테스트의 최소 단위로, “함수의 결과가 내가 생각한 값/타입과 일치하는가?”를 확인합니다. 모든 함수는 expect_로 시작합니다.

  • expect_equal(): 값이 같은지 확인
  • expect_type(): 데이터 타입이 맞는지 확인
  • expect_length(): 벡터의 길이가 맞는지 확인
  • expect_error(): 에러가 발생하는지 확인

3. 테스트 가능한 코드 작성 예시

테스트하기 좋은 함수는 하나의 명확한 작업만 수행해야 합니다.

# 데이터를 필터링하고 필요한 열만 선택하는 함수
wrangle_data <- function(data) {
  res <- data %>%
    filter(SAFFL == "Y") %>% 
    select(STUDYID, USUBJID, SEX, TRT01A, SAFFL)
  return(res)
}

# 빈도를 계산하는 함수
calculate_counts <- function(data) {
  res <- data %>%
    group_by(TRT01A, SEX) %>%
    count(name = "n")
  return(res)
}

4. 테스트 코드 작성하기

실제 데이터를 사용하여 위 함수들이 잘 작동하는지 테스트해 봅니다.

# 테스트용 샘플 데이터 생성
test_df <- tribble(
  ~STUDYID, ~USUBJID, ~SEX, ~TRT01A, ~SAFFL,
  "S01", "001", "M", "Placebo", "Y",
  "S01", "002", "F", "Placebo", "Y",
  "S01", "003", "M", "Active",  "N"
)

# 1. wrangle_data 테스트
test_that("wrangle_data가 올바르게 필터링하는가", {
  res <- wrangle_data(test_df)
  
  # SAFFL == 'Y'인 데이터만 남아야 하므로 행 개수는 2여야 함
  expect_equal(nrow(res), 2)
  # 선택한 5개의 열이 모두 있어야 함
  expect_length(names(res), 5)
  # 필터링 결과에 'N'이 없어야 함
  expect_false("N" %in% res$SAFFL)
})
Test passed 😸
# 2. calculate_counts 테스트
test_that("빈도 계산 결과가 정확한가", {
  res <- calculate_counts(wrangle_data(test_df))
  
  # Placebo군 남성(M)은 1명이어야 함
  expect_equal(res %>% filter(TRT01A == "Placebo", SEX == "M") %>% pull(n), 1)
})
Test passed 🎉

5. 테스트 조직화 (Folder Structure)

규모가 큰 프로젝트에서는 테스트 코드를 별도의 파일로 관리합니다. 통상적인 구조는 다음과 같습니다.

  • R/: 실제 함수가 담긴 파일들
  • tests/testthat/: test-로 시작하는 테스트 파일들
  • tests/testthat.R: 모든 테스트를 한 번에 실행하는 스크립트

챌린지

미니 프로젝트 7에서 작성한 adsl_counts 함수에 대한 테스트 코드를 작성해 보세요. 백분율(perc) 계산이 정확한지, 결측치가 포함된 데이터가 들어왔을 때 어떻게 반응하는지 등을 확인하는 테스트 케이스를 3개 이상 만들어 보세요.