testthat による単体テスト
test_that() ブロックを記述し、expectation を使って、devtools::test() でテストを実行します。
「testthat による単体テスト」はCoddyKit上の無料R Academyレッスンです。 これはレッスン3/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはR Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 R Academyコースには全4レッスンが含まれています。
ユニットテストを行う理由
ユニットテストは、個々の関数が正しく動作することを自動的に検証します。コードを変更した際のリグレッションを検出し、実行可能なドキュメントとして機能するとともに、安全にリファクタリングできるという自信を与えてくれます。testthatパッケージは、Rパッケージ向けの標準的なテストフレームワークです。
testthatをセットアップする
usethis::use_testthat()は、DESCRIPTIONのSuggestsにtestthatを追加し、tests/testthat/を作成するとともに、ランナースクリプトtests/testthat.Rを作成します。新しいパッケージを初期化するときに一度実行してください。
# usethis::use_testthat()
#
# Creates:
# tests/
# testthat.R <- runner (do not edit)
# testthat/
# (empty — write test files here)
#
# Updates DESCRIPTION:
# Suggests: testthat (>= 3.0.0)
# Config/testthat/edition: 3テストファイルを作成する
usethis::use_test('add')はtests/testthat/test-add.Rを作成します。慣例として、テストファイルはtest-{function_name}.Rという名前にします。各ファイルには、1つの関数または機能に対するテストをまとめます。
# usethis::use_test('add') # creates tests/testthat/test-add.R
#
# Content of test-add.R:
# test_that('add() returns correct sum', {
# expect_equal(add(1, 2), 3)
# expect_equal(add(-1, 1), 0)
# expect_equal(add(0.1, 0.2), 0.3, tolerance = 1e-7)
# })test_that()の構造
test_that('description', { ... })は、関連する期待値をまとめます。説明文字列は「test that ...」という文を完成させる内容にし、失敗メッセージを読んだときに役立つ程度に具体的にしてください。
# Good test_that descriptions:
# test_that('add() handles negative numbers', { ... })
# test_that('add() recycles length-1 vectors', { ... })
# test_that('add() returns NA when input contains NA', { ... })
#
# Bad (too vague):
# test_that('it works', { ... })
# test_that('test1', { ... })expect_equal()とexpect_identical()
expect_equal(actual, expected)は、浮動小数点数に対して数値的な許容誤差を用いてテストします。expect_identical(actual, expected)は、型を含めた完全な一致を要求します。多くの場合はexpect_equal()のほうが適しています。
# test_that('add() adds correctly', {
# expect_equal(add(1, 2), 3) # numeric equality
# expect_equal(add(0.1, 0.2), 0.3) # tolerance handles floating point
# expect_identical(add(1L, 2L), 3L) # exact type match: integer
# expect_identical(add(1.0, 2.0), 3.0) # exact type match: double
# })expect_error()とexpect_warning()
関数が正しいエラーや警告を生成することをテストします。エラーメッセージに一致させる正規表現パターンを渡してください。これにより、単に何らかのエラーが発生したかではなく、正しいエラーが発生したことを確認できます。
# test_that('add() validates input types', {
# expect_error(
# add('a', 2),
# regexp = 'numeric' # message must contain 'numeric'
# )
# expect_error(
# add(NULL, 1),
# regexp = 'numeric'
# )
# })
#
# test_that('sqrt() warns on negative input', {
# expect_warning(sqrt(-1))
# })expect_true()とexpect_false()
expect_true(expr)とexpect_false(expr)は、論理条件をテストします。1つの論理値を返す述語や条件をテストするときに使用してください。
# test_that('is_positive() returns correct logical', {
# expect_true(is_positive(5))
# expect_true(is_positive(0.001))
# expect_false(is_positive(0))
# expect_false(is_positive(-3))
# })
#
# # Also useful for vector tests:
# test_that('add() result has correct length', {
# result <- add(c(1,2,3), c(4,5,6))
# expect_true(length(result) == 3)
# })その他のexpectation関数
testthatには、さまざまな状況に対応する多くのexpectation関数があります。
expect_length(x, n)— ベクトルの長さを確認するexpect_type(x, 'double')— 基本型を確認するexpect_s3_class(x, 'data.frame')— S3クラスを確認するexpect_null(x)— NULLであることを確認するexpect_match(string, regexp)— 文字列のパターンを確認する
# test_that('add() output has correct type and length', {
# result <- add(c(1.0, 2.0), c(3.0, 4.0))
# expect_type(result, 'double')
# expect_length(result, 2)
# })
#
# test_that('summary_stats() returns a data frame', {
# result <- summary_stats(rnorm(100))
# expect_s3_class(result, 'data.frame')
# })devtools::test()でテストを実行する
devtools::test()(Ctrl+Shift+T)は、すべてのテストファイルを実行し、成功、失敗、警告の概要を表示します。個々のテストが失敗すると、失敗した期待値と、実際の値と期待される値が表示されます。
# devtools::test()
#
# Example output:
# == Testing mypackage ====================================
# v | OK F W S | Context
# v | 3 | add [0.1s]
# v | 4 | subtract [0.1s]
# x | 2 1 | multiply [0.2s]
# -- Failure (test-multiply.R:5): multiply() handles zero
# multiply(5, 0) not equal to 0.
# Actual: 5
# Expected: 0
# ==========================================================
# [ FAIL 1 | WARN 0 | SKIP 0 | PASS 9 ]covrでテストカバレッジを測定する
covr::package_coverage()は、テストによって実行されたパッケージの行の割合を測定します。covr::report()は、カバレッジされた行(緑)とカバレッジされていない行(赤)を示すHTMLレポートを開きます。カバレッジ80%以上を目指してください。
# library(covr)
# cov <- package_coverage()
# print(cov)
#
# Example output:
# mypackage Coverage: 87.50%
# R/add.R: 100.00%
# R/subtract.R: 100.00%
# R/utils.R: 62.50% <- needs more tests!
#
# covr::report() # interactive HTML report
# covr::zero_coverage(cov) # list uncovered linesエッジケースをテストする
優れたテストでは、正常系だけでなくエッジケースもカバーします。
- 空の入力:
numeric(0)、character(0) - NAを含む入力:関数はNAをそのまま伝播させますか、それとも処理しますか?
- 長さ1の入力と長さnの入力
- 境界値:0、負の数、非常に大きな値
- 不正な型:数値用の関数に文字列を渡すとどうなりますか?
# test_that('add() handles edge cases', {
# expect_equal(add(numeric(0), numeric(0)), numeric(0)) # empty
# expect_true(is.na(add(NA, 1))) # NA propagation
# expect_equal(add(1, c(1,2,3)), c(2,3,4)) # recycling
# expect_equal(add(.Machine$integer.max, 0L), # boundary
# .Machine$integer.max)
# })クイックチェック:expect_error()
expect_error(my_fn('bad'), regexp = 'invalid input')を呼び出します。このテストでは何を検証していますか?
単体テストのまとめ
testthatを使用したRパッケージのテスト:
usethis::use_testthat()— テスト基盤を一度だけセットアップしますusethis::use_test('fn')—tests/testthat/test-fn.Rを作成しますtest_that('description', {...})— 関連する期待値をまとめますexpect_equal()、expect_error()、expect_warning()、expect_true()、expect_false()— 基本的な期待値関数ですdevtools::test()— すべてのテストを実行します(Ctrl+Shift+T)covr::package_coverage()— テストカバレッジを測定します
よくある質問
「testthat による単体テスト」レッスンは無料ですか?
はい。「testthat による単体テスト」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、R Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 R Academyコースには全4レッスンが含まれています。
「testthat による単体テスト」で何を学びますか?
test_that() ブロックを記述し、expectation を使って、devtools::test() でテストを実行します。 ブラウザで直接実行するハンズオンコードでR Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。
R Academyを始めるのに経験は必要ですか?
事前経験は必要ありません。CoddyKitのR Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン3/4です。
「testthat による単体テスト」レッスンにはどのくらい時間がかかりますか?
ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。
このR Academyレッスンでコードを書いて実行できますか?
はい。すべてのR Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。