ここ数週間、私はtestthatパッケージの単体テスト機能を日々の仕事にますます統合してきました。
コードのどこかを変更するたびに、すべての単体テストを自動的に実行する autotest-feature が特に気に入っています。ただし、コードディレクトリに S4 定義 (クラス、ジェネリックメソッド、カスタムメソッド) が含まれている状況でtestthatの関数を使用すると、小さなバグまたは少なくとも望ましくない動作に遭遇したと思います。auto_test()
質問
この動作はバグの下に示されていますか、それとも使用時に何か間違ったことをしていauto_test()
ますか?
以下に、最初にいくつかのサンプル ファイルを作成する必要がある図を示します。
予選
作業ディレクトリを設定
setwd("testthat_autotest")
ディレクトリを確保する
dir.create("src", showWarnings=FALSE)
dir.create("tests", showWarnings=FALSE)
S4 参照クラス定義の作成
classes.R
ディレクトリにファイルを作成しますsrc
def <- c(
"setRefClass(",
" Class=\"MyClass\",",
" fields=list(",
" name=\"character\"",
" ),",
" methods=list(",
" getName=function(...) {",
" getName_ref(.self=.self, ...)",
" },",
" setName=function(...) {",
" setName_ref(.self=.self, ...)",
" }",
" )",
")"
)
write(def, file="src/classes.R")
ジェネリック メソッド定義の作成
generics.R
ディレクトリにファイルを作成しますsrc
def <- c(
"setGeneric(",
" name=\"getName_ref\",",
" signature=c(\".self\"),",
" def=function(",
" .self,",
" ...",
" ) {",
"standardGeneric(\"getName_ref\")",
" }",
")",
"",
"setGeneric(",
" name=\"setName_ref\",",
" signature=c(\".self\"),",
" def=function(",
" .self,",
" value,",
" ...",
" ) {",
" standardGeneric(\"setName_ref\")",
" }",
")"
)
write(def, file="src/generics.R")
カスタム メソッド定義の作成
methods.R
ディレクトリにファイルを作成しますsrc
def <- c(
"require(\"compiler\")",
"",
"setMethod(",
" f=\"getName_ref\",",
" signature=signature(.self=\"MyClass\"),",
" definition=cmpfun(function(",
" .self,",
" ...",
") {",
"out <- .self$name",
"return(out)",
"}, options=list(suppressAll=TRUE))",
")",
"setMethod(",
" f=\"setName_ref\",",
" signature=signature(.self=\"MyClass\"),",
" definition=cmpfun(function(",
" .self,",
" ...",
" ) {",
" .self$field(name=\"name\", value=value)",
" return(TRUE)",
" }, options=list(suppressAll=TRUE))",
")"
)
write(def, file="src/methods.R")
単体テストを作成する
ファイルtest_getName_ref.R
とtest_setName_ref.R
ディレクトリを作成しますtests
test <- c(
"require(\"testthat\")",
"",
"test_that(desc=\"test_getName_ref,.self=MyClass\",",
" code={",
" x.target <- \"some value\"",
" expect_that(",
" .self <- new(\"MyClass\", name=x.target),",
" is_a(\"MyClass\")",
" )",
" expect_that(",
" x.test <- .self$getName(),",
" is_a(\"character\")",
" )",
" expect_that(",
" x.test,",
" is_identical_to(x.target)",
" )",
" }",
")"
)
write(test, file="tests/test_getName_ref.R")
test <- c(
"require(\"testthat\")",
"",
"test_that(desc=\"test_setName_ref,.self=MyClass\",",
" code={",
" x.target <- \"some value\"",
" expect_that(",
" .self <- new(\"MyClass\"),",
" is_a(\"MyClass\")",
" )",
" expect_that(",
" .self$setName(value=x.target),",
" is_identical_to(TRUE)",
" )",
" expect_that(",
" .self$getName(),",
" is_identical_to(x.target)",
" )",
" }",
")"
)
write(test, file="tests/test_setName_ref.R")
図
実行auto_test()
するとエラーが発生します。
require("testthat")
auto_test(
code_path="src",
test_path="tests"
)
Loading required package: compiler
.1.2
1. Error: test_getName_ref,.self=MyClass ---------------------------------------
could not find function "getName_ref"
1: expect_that(x.test <- .self$getName(), is_a("character"))
2: condition(object)
3: str_c(class(x), collapse = ", ")
4: Filter(function(x) length(x) > 0, list(...))
5: sapply(x, f)
6: lapply(X = X, FUN = FUN, ...)
7: .self$getName()
2. Error: test_setName_ref,.self=MyClass ---------------------------------------
could not find function "setName_ref"
1: expect_that(.self$setName(value = x.target), is_identical_to(TRUE))
2: condition(object)
3: all.equal(expected, actual)
4: all.equal.default(expected, actual)
5: all.equal.raw(target, current, ...)
6: attr.all.equal(target, current, ...)
7: mode(current)
8: .self$setName(value = x.target)
私の最初の推測ではtestthat::source_dir()
、おそらく引数のために、ソーシングを処理する方法に関係があるのではないでしょenv
うか?
続行するには、CRTL + BREAK で自動テストをオフにする必要があります (Windowsの場合。他のプラットフォームを使用している場合は、この記事のセクション「自動テスト」を参照してください)。
ここで、最初にコードを手動でソースして実行するtest_dir()
と、すべて正常に動作するように見えます。
require("testthat")
sapply(list.files("src", full.names=TRUE), source)
test_dir("tests")
......
You are a coding rockstar!
>