iBetter Books
수정

shinytest2로 테스트

앱이 커질수록 수동으로 클릭해서 확인하는 방식은 한계에 부딪힙니다. 필터를 바꿨을 때 차트가 제대로 갱신되는지, 파일을 업로드했을 때 테이블이 나타나는지를 코드로 검증할 수 있으면 배포 전 자신감이 생깁니다. shinytest2는 Shiny 앱 전용 자동화 테스트 패키지입니다.

설치

install.packages("shinytest2")

shinytest2는 내부적으로 chromote(Chromium 기반 브라우저)를 씁니다. Google Chrome 또는 Chromium이 설치되어 있어야 합니다.

첫 테스트 만들기

테스트 파일은 tests/testthat/ 폴더에 위치합니다.

modular-app/
├── app.R
├── global.R
└── tests/
    └── testthat/
        └── test-app.R

shinytest2::record_test()를 실행하면 브라우저가 열리고, 클릭하는 과정을 녹화해 테스트 코드를 자동 생성합니다.

# RStudio 콘솔에서 실행
shinytest2::record_test("modular-app")

테스트 파일 구조

# 새 파일: modular-app/tests/testthat/test-app.R
library(shinytest2)

test_that("필터 변경 시 차트 갱신", {
  app <- AppDriver$new(
    app_dir = ".",          # app.R이 있는 폴더
    name    = "filter-test",
    height  = 800,
    width   = 1200
  )

  # 초기 상태 스냅샷
  app$expect_values()

  # 지역 선택 변경
  app$set_inputs(region = "부산")

  # 변경 후 상태 스냅샷 — 처음 실행 시 기준값 저장
  app$expect_values()

  app$stop()
})

expect_values()는 현재 입력값, 출력값, 내보내기 값을 캡처해서 저장합니다. 이후 테스트를 다시 실행하면 저장된 기준값과 비교해 달라진 부분이 있으면 실패를 알립니다.

스냅샷 테스트

차트 이미지나 테이블 HTML을 비교하는 스냅샷 테스트를 추가합니다.

# 수정: modular-app/tests/testthat/test-app.R
library(shinytest2)

test_that("데이터 테이블 행 수 확인", {
  app <- AppDriver$new(".", name = "table-test")

  # 서울만 선택
  app$set_inputs(region = "서울")
  app$wait_for_idle(timeout = 5000)

  # 테이블 출력값 가져오기
  tbl_html <- app$get_value(output = "data_table")

  # 테이블에 "서울"이 포함되었는지 확인
  expect_true(grepl("서울", tbl_html))

  app$stop()
})

test_that("연도 범위 변경 시 차트 타이틀 확인", {
  app <- AppDriver$new(".", name = "year-test")

  app$set_inputs(
    region     = "대구",
    year_range = c(2020, 2022)
  )
  app$wait_for_idle()

  # 현재 입력/출력 값 스냅샷
  app$expect_values(
    input  = c("region", "year_range"),
    output = "trend_chart"
  )

  app$stop()
})

expect_values() vs expect_screenshot()

방법 특징 적합한 경우
expect_values() 입출력 JSON 비교 데이터 로직 검증
expect_screenshot() 화면 이미지 비교 UI 레이아웃 검증

expect_screenshot()은 운영체제별로 렌더링이 달라 불안정할 수 있습니다. 데이터 검증에는 expect_values()를 주로 씁니다.

테스트 실행

# RStudio 터미널 또는 콘솔에서
testthat::test_dir("tests/testthat")

# 또는 shinytest2 전용
shinytest2::test_app("modular-app")

처음 실행하면 스냅샷 파일(tests/testthat/_snaps/)이 생성됩니다. 두 번째 실행부터 기준값과 비교합니다.

스냅샷 업데이트

의도적으로 출력을 바꿨을 때는 기준 스냅샷을 갱신합니다.

# 스냅샷 강제 업데이트
testthat::snapshot_accept()

GitHub Actions와 연동

.github/workflows/shiny-test.yaml을 만들어 PR마다 자동 테스트를 실행합니다.

# 새 파일: .github/workflows/shiny-test.yamlname: Shiny App Testson: [push, pull_request]jobs:  test:    runs-on: ubuntu-latest    steps:      - uses: actions/checkout@v4      - uses: r-lib/actions/setup-r@v2        with:          r-version: "4.3"      - uses: r-lib/actions/setup-r-dependencies@v2        with:          packages: |            shiny            shinytest2            bslib            tidyverse            plotly            DT      - name: Install Chromium        run: chromote::install_chrome()        shell: Rscript {0}      - name: Run Tests        run: shinytest2::test_app("modular-app")        shell: Rscript {0}

이제 코드를 Push할 때마다 GitHub가 자동으로 앱을 실행하고 테스트 결과를 알려줍니다. 테스트가 통과해야만 머지할 수 있도록 설정하면 회귀 버그를 배포 전에 잡을 수 있습니다.