vignettes/d-automated-testing.Rmd
d-automated-testing.Rmd
Automated testing is essential in modern software development. In the context of psychological experiment development, automated tests help you to ensure the robustness of your experiment’s implementation without having to invest tedious hours in manual testing.
To construct an automated test in psychTestR, you first need a test
implementation saved in a file named app.R
. Suppose we’ve
implemented the following minimal example:
library(psychTestR)
display_number <- function(i) {
one_button_page(paste("Page", i))
}
timeline <- join(
lapply(1:5, display_number),
final_page("End.")
)
test <- make_test(timeline, opt = test_options("Minimal test", "password"))
Now make a new file, called test.R
(or whatever you
want). Within this file, we construct an automated tester using the
AppTester
class:
library(psychTestR)
dir <- system.file("demos/minimal-test", package = "psychTestR", mustWork = TRUE)
app <- AppTester$new(dir)
We’ve put a sample app.R
file in the psychTestR package,
and the above example finds that file using system.file
; to
point the code to your own app.R
file, simply replace the
system.file
call with the path to the directory containing
your app.R
file.
This AppTester
object instantiates a headless browser
which then navigates your psychTestR test, just like a real participant.
This object has various methods that are useful for interacting with the
psychTestR test and probing its internal state. For example, we can
probe the current UI text:
app$get_ui_text()
## [1] "Page 1 Next"
We can click the “Next” button:
app$click_next()
app$get_ui_text()
## [1] "Page 2 Next"
We can pull local variables:
app$get_locals()
## $.module
## NULL
##
## $.results_label
## [1] "results"
We can run assertions that throw an error if a given condition is not satisfied:
app$expect_ui_text("Page 2 Next")
And we can combine all of this together into an automated script that checks that our test runs as expected:
library(testthat) # for the expect_equal function
app <- AppTester$new(dir)
app$expect_title("Minimal test") # check the test's title
expect_equal(
app$get_locals(),
list(.module = NULL,
.results_label = "results")
)
for (i in 1:5) {
app$expect_ui_text(sprintf("Page %i Next", i))
app$click_next()
}
app$expect_ui_text("End.")
app$stop() # shuts down the testing process
See ?AppTester
for more information on automated testing
utilities.
The above example was rather simple, but automated testing comes into its own with more complex implementations. Here is an example from the test suite in the psychTestR package, designed to check that nested modules work as intended.
context("test_modules")
test_that("main", {
app <- AppTester$new("apps/modules")
app$expect_ui_text("We begin in the global environment. Next")
app$get_locals() %>% expect_equal(list(.module = NULL,
.results_label = "results"))
app$click_next()
app$expect_ui_text("We've now entered the parent environment. Next")
app$get_locals() %>% expect_equal(list(.module = "parent",
.results_label = "parent"))
app$click_next()
app$expect_ui_text("In the parent environment, we define a local variable, x = 42. Next")
app$get_locals()$x %>% expect_equal(42)
app$click_next()
app$expect_ui_text("Now we enter the child environment. Next")
app$get_locals() %>% expect_equal(list(.module = "child",
.results_label = "parent.child"))
app$click_next()
app$expect_ui_text("We can't see x any more: x is now NULL. Next")
app$get_locals()$x %>% expect_equal(NULL)
app$click_next()
app$expect_ui_text("We can set it to a new value, though: x = 65. Next")
app$get_locals()$x %>% expect_equal(65)
app$click_next()
app$expect_ui_text("Now we return to the parent environment. Next")
app$get_locals() %>% expect_equal(list(.module = "parent",
.results_label = "parent",
x = 42))
app$click_next()
app$expect_ui_text("We see that x = 42 again. Next")
app$click_next()
app$expect_ui_text("Now we return to the global environment. Next")
app$get_locals() %>% expect_equal(list(.module = NULL,
.results_label = "results"))
app$click_next()
app$expect_ui_text("We see that x is is NULL again.")
app$get_locals()$x %>% expect_equal(NULL)
app$get_results() %>% as.list() %>% expect_equal(list(
parent = list(x = 42),
parent.child = list(x = 65)
))
app$stop()
})
library(psychTestR)
elts <- join(
one_button_page("We begin in the global environment."),
module(
"parent",
one_button_page("We've now entered the parent environment."),
code_block(function(state, ...) {
x <- 42
set_local("x", x, state)
save_result(state, "x", x)
}),
one_button_page("In the parent environment, we define a local variable, x = 42."),
module(
"child",
one_button_page("Now we enter the child environment."),
one_button_page("We can't see x any more: x is now NULL."),
code_block(function(state, ...) {
x <- 65
set_local("x", x, state)
save_result(state, "x", x)
}),
one_button_page("We can set it to a new value, though: x = 65.")
),
one_button_page("Now we return to the parent environment."),
one_button_page("We see that x = 42 again.")
),
one_button_page("Now we return to the global environment."),
final_page("We see that x is is NULL again.")
)
make_test(elts)