escapeR was first created for an Ecological Statistics
course. The bundled rooms therefore use ecological examples: field
counts, survey data, detection, modelling, and reproducible reporting.
The package architecture is more general than that, however. An escape
room is just a sequence of small tasks where students use R to unlock
the next step.
This vignette explains how to create rooms in any theme, such as
medicine, agriculture, social sciences, economics, psychology, or
environmental policy. It also shows how to bundle those rooms into a
shareable room pack that can be submitted to the escapeR
GitHub repository.
New rooms and quests are welcome. To contribute them to
escapeR, submit a GitHub pull request containing the
room-pack definition, its registration entry, tests, and any data files
it needs. The complete process is described below.
For coursework submissions through Moodle, start with Make your own room: a simple
coursework guide. It explains how to edit and submit a single
.R file. The guide below describes the fuller route for
creating themed packs and contributing them to the package.
Every room is built from eleven components, although some are optional:
id: a short unique identifier, at most 8 characters,
such as "bpmean" or "yield".module: the broad teaching module, such as
"Medicine" or "Agriculture".title: the room title shown to students.learning_goal: what the student should practise.introduction: the story or setting.challenge: the task the student must solve.hints: one or more hints. Repeated calls to
hint() reveal them in order.correct_result: the expected answer for simple numeric
or text answers.checker: an optional custom function for more complex
answer checking.success: the message shown after a correct answer.failure: an optional message shown after a wrong
answer.Most beginner rooms only need a simple correct_result.
For example, if the correct answer is 130,
escapeR checks numeric submissions with a small tolerance.
If the correct answer is "high", escapeR
checks text after trimming spaces and ignoring case. Use
checker instead of correct_result when you
need more control.
Start with a familiar domain and a small learning goal. Good first rooms are short, concrete, and assess one idea at a time.
Examples:
For a first contribution, aim for two or three rooms. That is enough to create a small story arc while keeping the pull request easy to review.
The example below creates a tiny medicine-themed escape room. The first room asks students to create a vector and calculate a mean. The second asks them to use a logical comparison and submit a category. The third asks for a simple proportion.
library(escapeR)
bpmean <- new_room(
id = "bpmean",
module = "Medicine",
title = "The Clinic Intake",
learning_goal = "Create a numeric vector and calculate its mean.",
introduction = paste(
"A clinic intake sheet lists systolic blood pressure readings for",
"three patients seen before lunch."
),
challenge = paste(
"Create a vector with the values 120, 124, 111, 182, 130, and 145.",
"What is the average systolic blood pressure? Submit the mean, rounded to 1 decimal."
),
hints = c(
"Use c() to combine multiple readings into one vector.",
"Use mean() to calculate the average of a numeric vector."
),
correct_result = 133.3,
success = "The intake sheet is complete, and the first cabinet opens.",
failure = "Not quite, young doctor. Check that all readings are included before taking the mean."
)
riskcat <- new_room(
id = "riskcat",
module = "Medicine",
title = "The Risk Label",
learning_goal = "Use a logical comparison to classify a value.",
introduction = paste(
"A label printer waits beside the triage desk.",
"It needs the correct risk category for the next patient."
),
challenge = paste(
"A systolic blood pressure of 145 is considered high if it is above 140.",
"Submit high or normal."
),
hints = c(
"Ask R whether 145 > 140.",
"If the comparison is TRUE, the category requested by the room is high."
),
correct_result = "high",
success = "The label prints clearly, and the triage desk unlocks."
)
posprop <- new_room(
id = "posprop",
module = "Medicine",
title = "The Test Result Board",
learning_goal = "Calculate a proportion from counts.",
introduction = paste(
"A result board shows 8 positive tests out of 40 tests performed.",
"The ward door asks for the positive proportion."
),
challenge = "Submit the proportion of tests that were positive.",
hints = c(
"A proportion is part divided by whole.",
"In R, calculate 8 / 40."
),
correct_result = 0.2,
success = "The board accepts the proportion, and the ward door opens."
)Notice that each authoring object is self-contained. It contains
everything needed to review, edit, translate, and reuse the room. During
play, escapeR exposes the introduction, challenge, learning
goal, and hints, but keeps the correct result and checker out of the
playable room object.
A room pack is the unit that is easiest to share. It contains a pack ID, a short description, the rooms, and one or more named escape sequences. A sequence is just an ordered vector of room IDs.
medicine_pack <- new_room_pack(
id = "medpack",
title = "Introductory medicine rooms",
description = paste(
"A small set of medicine-themed rooms for practising vectors,",
"means, comparisons, and proportions."
),
rooms = list(bpmean, riskcat, posprop),
escapes = list(
medmini = c("bpmean", "riskcat"),
medfull = c("bpmean", "riskcat", "posprop")
)
)The pack can contain more than one escape sequence. In this example,
medmini is a two-room activity and medfull
uses all three rooms.
Before submitting a pull request, register the pack in your R session, inspect the rooms and escape sequences, and then start a test game.
register_room_pack(medicine_pack, replace = TRUE)
list_rooms()
#> room id
#> console 1 console
#> vector 2 vector
#> finddata 3 finddata
#> datatab 4 datatab
#> columns 5 columns
#> missing 6 missing
#> plotwin 7 plotwin
#> habitat 8 habitat
#> hidden 9 hidden
#> subset 10 subset
#> sorting 11 sorting
#> model 12 model
#> resid 13 resid
#> predict 14 predict
#> detectp 15 detectp
#> detect 16 detect
#> trunc 17 trunc
#> comment 18 comment
#> webglm 19 webglm
#> quarto 20 quarto
#> en08 21 en08
#> en09 22 en09
#> posprop 23 posprop
#> en10 24 en10
#> en11 25 en11
#> en12 26 en12
#> en13 27 en13
#> bpmean 28 bpmean
#> en14 29 en14
#> en15 30 en15
#> en16 31 en16
#> en17 32 en17
#> en18 33 en18
#> en19 34 en19
#> riskcat 35 riskcat
#> en20 36 en20
#> en21 37 en21
#> en22 38 en22
#> en23 39 en23
#> en24 40 en24
#> en01 41 en01
#> en25 42 en25
#> en02 43 en02
#> en03 44 en03
#> en04 45 en04
#> en05 46 en05
#> en06 47 en06
#> en07 48 en07
#> module
#> console R foundations
#> vector R foundations
#> finddata Data import and inspection
#> datatab Data import and inspection
#> columns Data import and inspection
#> missing Data import and inspection
#> plotwin Visualisation
#> habitat Visualisation
#> hidden Visualisation
#> subset Data manipulation
#> sorting Data manipulation
#> model Ecological modelling
#> resid Ecological modelling
#> predict Ecological modelling
#> detectp Distance sampling
#> detect Distance sampling
#> trunc Distance sampling
#> comment Reproducible workflow
#> webglm Reproducible workflow
#> quarto Reproducible workflow
#> en08 Ecologia Num<U+00E9>rica - Lecture 08: Experimental design and pseudoreplication
#> en09 Ecologia Num<U+00E9>rica - Lecture 09: Exploratory graphics and transformations
#> posprop Medicine
#> en10 Ecologia Num<U+00E9>rica - Lecture 10: Hypothesis testing and p-values
#> en11 Ecologia Num<U+00E9>rica - Lecture 11: Assumptions and the one-sample t-test
#> en12 Ecologia Num<U+00E9>rica - Lecture 12: One- and two-sample tests: signed ranks
#> en13 Ecologia Num<U+00E9>rica - Lecture 13: Paired tests and one-way ANOVA
#> bpmean Medicine
#> en14 Ecologia Num<U+00E9>rica - Lecture 14: Multiple comparisons and factorial ANOVA
#> en15 Ecologia Num<U+00E9>rica - Lecture 15: Blocks, repeated measures, and nested designs
#> en16 Ecologia Num<U+00E9>rica - Lecture 16: Simple and multiple linear regression
#> en17 Ecologia Num<U+00E9>rica - Lecture 17: Factors in regression and model diagnostics
#> en18 Ecologia Num<U+00E9>rica - Lecture 18: GLMs, links, and model selection
#> en19 Ecologia Num<U+00E9>rica - Lecture 19: Generalized additive models
#> riskcat Medicine
#> en20 Ecologia Num<U+00E9>rica - Lecture 20: Correlation and contingency tables
#> en21 Ecologia Num<U+00E9>rica - Lecture 21: Log-linear models and maximum likelihood
#> en22 Ecologia Num<U+00E9>rica - Lecture 22: Multivariate data and classification
#> en23 Ecologia Num<U+00E9>rica - Lecture 23: Distances and hierarchical clustering
#> en24 Ecologia Num<U+00E9>rica - Lecture 24: Non-hierarchical clustering and ordination
#> en01 Ecologia Num<U+00E9>rica - Class 01
#> en25 Ecologia Num<U+00E9>rica - Lecture 25: PCA and other ordination methods
#> en02 Ecologia Num<U+00E9>rica - Lecture 02: Critical thinking and observation filters
#> en03 Ecologia Num<U+00E9>rica - Lecture 03: Scientific questions and falsifiable hypotheses
#> en04 Ecologia Num<U+00E9>rica - Lecture 04: Variable types and probability rules
#> en05 Ecologia Num<U+00E9>rica - Lecture 05: Random variables and distributions
#> en06 Ecologia Num<U+00E9>rica - Lecture 06: Distribution functions in R
#> en07 Ecologia Num<U+00E9>rica - Lecture 07: Sampling and stratification
#> title
#> console The Console Door
#> vector The Vector Cabinet
#> finddata Finding Data
#> datatab The Data Table Hatch
#> columns The Column Scanner
#> missing The Missing Value Mirror
#> plotwin The Plotting Window
#> habitat The Habitat Mosaic
#> hidden The Hidden Data
#> subset The Subsetting Lock
#> sorting The Sorting Staircase
#> model The Model Room
#> resid The Residual Drawer
#> predict The Prediction Lantern
#> detectp The Observation Filter
#> detect The Detection Counter
#> trunc The Truncation Gate
#> comment The Comment Cipher
#> webglm The Web Data Model
#> quarto The Quarto Exit
#> en08 The Aquarium Illusion
#> en09 The Standardisation Mirror
#> posprop The Test Result Board
#> en10 The Sign Test Lock
#> en11 The Reference Growth Chamber
#> en12 The Ranked Shells
#> en13 The Three Meadows
#> bpmean The Clinic Intake
#> en14 The Interaction Trap
#> en15 The Blocked Greenhouse
#> en16 The Regression Staircase
#> en17 The Residual Window
#> en18 The Logistic Gate
#> en19 The Curved Forest
#> riskcat The Risk Label
#> en20 The Independence Table
#> en21 The Likelihood Nest
#> en22 The Community Matrix
#> en23 The Shared Species Lock
#> en24 The Two Shoals
#> en01 The First Practical Class
#> en25 The Final Principal Door
#> en02 The Invisible Birds
#> en03 The River Hypothesis
#> en04 The Two Habitat Gates
#> en05 The Nest Lottery
#> en06 The Temperature Vault
#> en07 The Unequal Habitats
#> learning_goal
#> console Use R as a calculator and learn expression order.
#> vector Create vectors and summarize them with functions.
#> finddata Find and inspect built-in R datasets.
#> datatab Read and inspect a CSV data set.
#> columns Identify variables in a data frame.
#> missing Check data quality before analysis.
#> plotwin Make a basic plot and read a visual pattern.
#> habitat Use tables to summarize categorical variables before plotting.
#> hidden Discover that plotted data can reveal structure hidden in plain text.
#> subset Subset data frames using logical conditions.
#> sorting Order data and inspect extreme observations.
#> model Fit and interpret a simple linear model.
#> resid Understand that fitted models leave residual variation.
#> predict Use a fitted model to predict for a new ecological setting.
#> detectp Connect observed data to an imperfect observation process.
#> detect Summarize detections as observed outcomes.
#> trunc Think about distance cutoffs and retained observations.
#> comment Recognize comments as part of readable R code.
#> webglm Read data from a web link and fit a Poisson GLM.
#> quarto Recognize reproducible reports as part of the analysis workflow.
#> en08 Identify experimental units and avoid counting subsamples as treatment replicates.
#> en09 Centre and scale measurements using the sample standard deviation.
#> posprop Calculate a proportion from counts.
#> en10 Calculate an exact two-sided binomial p-value under a stated null hypothesis.
#> en11 Compute a one-sample t statistic while recognizing independence and Normality assumptions.
#> en12 Calculate the positive-rank sum in a one-sample Wilcoxon signed-rank test.
#> en13 Fit a one-way ANOVA and extract the between-group F statistic.
#> bpmean Create a numeric vector and calculate its mean.
#> en14 Recognize a factorial interaction through a difference of treatment effects.
#> en15 Account for matched blocks by analysing differences within each block.
#> en16 Fit a linear regression and interpret its slope in ecological units.
#> en17 Calculate residuals from a fitted model and identify the largest absolute residual.
#> en18 Transform a binomial GLM linear predictor into a probability on the response scale.
#> en19 Recognize when a smooth ecological response calls for a GAM.
#> riskcat Use a logical comparison to classify a value.
#> en20 Compute an expected contingency-table count under independence.
#> en21 Find a Bernoulli maximum-likelihood estimate using a grid search.
#> en22 Organize sites as rows and species as columns, then summarize each species.
#> en23 Calculate Jaccard dissimilarity for presence-absence data while excluding joint absences.
#> en24 Run k-means with explicit initial centres and interpret a centre independently of its label.
#> en01 Use R arithmetic, square roots, and unit conversion to calculate travel time.
#> en25 Fit a PCA and calculate the proportion of variance explained by its first component.
#> en02 Distinguish observed counts from abundance when detection is imperfect.
#> en03 Translate a falsifiable ecological hypothesis into a numerical prediction.
#> en04 Calculate the probability of a union without double-counting an intersection.
#> en05 Calculate a binomial probability and recognize its assumptions.
#> en06 Use a cumulative distribution function to calculate an upper-tail probability.
#> en07 Weight stratum means by the sizes of the strata.
list_escapes()
#> id pack_id
#> en2026 en2026 enpack
#> medmini medmini medpack
#> medfull medfull medpack
#> enintro enintro enpack
#> rooms
#> en2026 en01, en02, en03, en04, en05, en06, en07, en08, en09, en10, en11, en12, en13, en14, en15, en16, en17, en18, en19, en20, en21, en22, en23, en24, en25
#> medmini bpmean, riskcat
#> medfull bpmean, riskcat, posprop
#> enintro en01
medicine_escape <- build_escape("medfull")
medicine_escape$room_ids
#> [1] "bpmean" "riskcat" "posprop"To play it interactively:
escape(player = "demo_medicine", reset = TRUE, escape = medicine_escape)
# Room 1 answer
submit(133.3)
# Room 2 answer
submit("high")
# Room 3 answer
submit(0.2)When developing a pack, test wrong answers as well as correct ones. Where relevant, try numeric values, text, and the kinds of R objects your checker is designed to accept. Good failure messages help students recover and may offer a small nudge without giving away the solution.
Sometimes a room has several acceptable answers. For example, a
social-science room might accept "increase",
"increased", or "up". In that case, provide a
checker function instead of
correct_result.
trend <- new_room(
id = "trend",
module = "Social sciences",
title = "The Survey Trend",
learning_goal = "Interpret a direction of change.",
introduction = "A survey dashboard compares this year with last year.",
challenge = "Satisfaction rose from 62 to 70 percent. Submit the direction of change.",
hints = c(
"Compare the second value with the first.",
"Several words can describe an upward change."
),
checker = function(answer) {
tolower(trimws(as.character(answer))) %in% c("increase", "increased", "up", "rose")
},
success = "The dashboard accepts the trend.",
failure = "Try describing whether the second value is higher or lower than the first."
)Custom checkers should be short and predictable. They must return
TRUE only when the answer should unlock the room.
An accepted contribution must do more than define a pack: it must
also add the pack to the package catalogue and test the student-facing
submission path. The steps below describe the complete route from a
local room pack to rooms and named escapes that are available
immediately after library(escapeR).
Place the pack in a single R source file. A good file name uses the pack theme, for example:
R/room-pack-medicine.R
R/room-pack-agriculture.R
R/room-pack-social.R
The file should define one function that takes no arguments and returns the pack. The function is an internal package factory, so it does not need to be exported. For the medicine example, the submitted file would look like this:
medicine_room_pack <- function() {
bpmean <- new_room(
id = "bpmean",
module = "Medicine",
title = "The Clinic Intake",
learning_goal = "Create a numeric vector and calculate its mean.",
introduction = paste(
"A clinic intake sheet lists systolic blood pressure readings for",
"three patients seen before lunch."
),
challenge = paste(
"Create a vector with the values 120, 124, 111, 182, 130, and 145.",
"What is the average systolic blood pressure? Submit the mean, rounded to 1 decimal."
),
hints = c(
"Use c() to combine multiple readings into one vector.",
"Use mean() to calculate the average of a numeric vector."
),
correct_result = 133.3,
success = "The intake sheet is complete, and the first cabinet opens.",
failure = "Not quite, young doctor. Check that all readings are included before taking the mean."
)
riskcat <- new_room(
id = "riskcat",
module = "Medicine",
title = "The Risk Label",
learning_goal = "Use a logical comparison to classify a value.",
introduction = "A label printer waits beside the triage desk.",
challenge = paste(
"A systolic blood pressure of 145 is considered high if it is above 140.",
"Submit high or normal."
),
hints = c(
"Ask R whether 145 > 140.",
"If the comparison is TRUE, the category requested by the room is high."
),
correct_result = "high",
success = "The label prints clearly, and the triage desk unlocks."
)
posprop <- new_room(
id = "posprop",
module = "Medicine",
title = "The Test Result Board",
learning_goal = "Calculate a proportion from counts.",
introduction = paste(
"A result board shows 8 positive tests out of 40 tests performed.",
"The ward door asks for the positive proportion."
),
challenge = "Submit the proportion of tests that were positive.",
hints = c(
"A proportion is part divided by whole.",
"In R, calculate 8 / 40."
),
correct_result = 0.2,
success = "The board accepts the proportion, and the ward door opens."
)
new_room_pack(
id = "medpack",
title = "Introductory medicine rooms",
description = paste(
"A small set of medicine-themed rooms for practising vectors,",
"means, comparisons, and proportions."
),
rooms = list(bpmean, riskcat, posprop),
escapes = list(
medmini = c("bpmean", "riskcat"),
medfull = c("bpmean", "riskcat", "posprop")
)
)
}Room IDs must be unique across all bundled and contributed rooms. Pack IDs and named escape IDs must likewise avoid existing registered IDs. Keep every ID to at most eight letters, numbers, or underscores, beginning with a letter.
Defining medicine_room_pack() is not enough to make its
rooms available to users. Add a call to the factory inside
.bundled_room_packs() in
R/bundled-packs.R:
If the catalogue already contains packs, retain them and add the new factory as another list element:
When escapeR loads, its .onLoad() hook
registers every pack in this list. Consequently, users do not need to
call register_room_pack() for a bundled contribution: its
rooms appear in list_rooms(), its named sequences appear in
list_escapes(), and those sequences work with
build_escape() immediately.
Do not add a registration call to an example or vignette as a substitute for the catalogue entry. Such a call affects only the R session in which that example happens to run.
If a room needs a small data file, add it under
inst/extdata/ and locate it in the challenge with
escapeR_file(). Use a distinctive file name to avoid
collisions. The room and its tests must not depend on files that exist
only on the contributor’s computer.
For example, a room could direct students to read:
Keep contributed data small, document their origin and licence in the pull request, and include only data that the package is permitted to redistribute.
Add a focused test file such as
tests/testthat/test-room-pack-medicine.R. Tests should
verify permanent registration and should submit answers through the
exported game interface. That is more useful than calling a room’s
internal checker directly because it also tests registration, escape
construction, room order, progress, and submission handling.
testthat is an R package for writing automated checks of
code behaviour. A call to test_that() groups checks for one
behaviour and gives that group a descriptive name. Inside it, functions
beginning with expect_ state what should be true: for
example, expect_equal() compares values,
expect_true() requires a true result, and
expect_error() requires an error. When an expectation
fails, testthat reports the failed expectation and its
location in the test file. This makes it easier to detect when a later
code change breaks a room that previously worked.
For example:
test_that("medicine room pack is registered and playable", {
expect_true(all(
c("bpmean", "riskcat", "posprop") %in% list_rooms()$id
))
expect_true(all(c("medmini", "medfull") %in% list_escapes()$id))
medicine_escape <- build_escape("medfull")
expect_equal(
medicine_escape$room_ids,
c("bpmean", "riskcat", "posprop")
)
player <- paste0("medicine_test_", Sys.getpid())
escape(player = player, reset = TRUE, escape = medicine_escape)
expect_false(submit(100))
expect_true(submit(133.3))
expect_true(submit(" HIGH "))
expect_true(submit(0.2))
})Use a unique test-player name so repeated test runs do not reuse another test’s progress. Test at least one incorrect answer and every correct answer in the sequence. If a room uses a custom checker, include representative accepted and rejected submissions for that checker.
The tests use testthat. Its expectations describe the
required behaviour:
expect_true(x > 0) # a condition holds
expect_false(is.na(x)) # a condition does not hold
expect_equal(result, 4) # two values are equal
expect_error(log("text")) # an error is triggered
expect_warning(sqrt(-1)) # a warning is triggered
expect_type(x, "double") # typeof(x) is "double"Package tests belong under tests/testthat/. Run them
locally with:
If testthat is new to you, see the testthat primer at the end of this
vignette. It explains the structure of a test, the main expectation
functions and their arguments, and how to interpret a failure.
Run the full package checks before opening the pull request:
Also open the rendered vignettes and play the contributed escape once in a fresh R session. Check that the story, hints, failure messages, room order, and all required files make sense from a student’s perspective.
The pull request should contain:
R/bundled-packs.R.inst/extdata/, with source
and licensing details.In the pull-request description, summarize the learning goals, list
the new room and escape IDs, explain any data provenance, and report the
result of devtools::check(). Reviewers can then evaluate
both the teaching design and the technical integration from one
self-contained contribution.
Write rooms for learners, not for answer keys. A useful room should:
The ecology rooms bundled with escapeR are one theme.
The same architecture can support other domains, as long as each room
remains a small, testable R learning step.
testthat is an R package for checking automatically that
code behaves as intended. Each test runs some code and compares the
observed result with an expected result. A passing test is normally
quiet; a failing test identifies the expectation that failed, shows the
difference when possible, and reports the file and line where it
occurred.
Tests are useful because a room that works today can be broken accidentally by a later change to registration, answer checking, or player progress. Re-running the tests checks the old behaviour without requiring someone to play every escape manually.
In an R package, test files conventionally live under
tests/testthat/ and have names beginning with
test-, for example:
tests/testthat/test-room-pack-medicine.R
The package lists testthat under Suggests
in DESCRIPTION. The line
Config/testthat/edition: 3 tells testthat that
the package uses its third edition of testing behaviour.
Most tests follow three small steps:
expect_*()
functions to state the expected result.test_that(description, code) groups related
expectations:
test_that("a correct answer advances the player", {
# Arrange
player <- paste0("medicine_test_", Sys.getpid())
medicine_escape <- build_escape("medmini")
escape(player = player, reset = TRUE, escape = medicine_escape)
# Act and assert
expect_false(submit(100))
expect_true(submit(133.3))
})The first argument is a short description of the behaviour. The
second is the block of R code inside {}. If an expectation
fails, that description helps identify what stopped working.
An expectation receives the value or expression being checked as its first argument. Common expectations include:
expect_true(object) and
expect_false(object) require object to
evaluate to exactly TRUE or FALSE.expect_equal(object, expected, tolerance = NULL)
compares values while allowing small numerical differences. Use
tolerance when floating-point calculations need an explicit
margin.expect_identical(object, expected) is stricter: values
must have the same type and attributes as well as the same
contents.expect_length(object, n) requires an object to have
length n.expect_type(object, type) checks the base type returned
by typeof(), such as "double",
"integer", or "list".expect_s3_class(object, class) checks that an object
inherits from an S3 class.expect_match(object, regexp) checks text against a
regular expression.expect_error(object, regexp = NULL),
expect_warning(), and expect_message() require
an expression to produce the named condition. A regexp can
also check part of its message.expect_no_error(object) requires an expression to
finish without an error.Many expectations also accept info, an optional
explanatory message added to a failure report. It can be helpful when
the reason for an expectation is not obvious:
expect_equal(
medicine_escape$room_ids,
c("bpmean", "riskcat", "posprop"),
info = "The full medicine escape must preserve its teaching order"
)Prefer the most specific expectation that expresses the intended
behaviour. For example, expect_equal(answer, 4) usually
produces a clearer failure than
expect_true(answer == 4).
To test that invalid input is rejected, put the expression that
should fail inside expect_error():
The expression is evaluated by the expectation; it is not run
beforehand. Omitting regexp checks only that some error
occurs. Including it also checks that the message contains the expected
text.
Computers sometimes store calculated decimal values with tiny rounding differences. In those cases, compare with a tolerance:
Do not use a large tolerance merely to make a test pass: it should reflect the precision that the room genuinely promises.
From the package project, the usual command is:
This loads the package’s development code and runs the files under
tests/testthat/. During development,
devtools::test(filter = "medicine") can run only test files
whose names match medicine. The filter value
matches the part after test- and before
.R.
Two lower-level testthat functions are also useful:
testthat::test_file("tests/testthat/test-room-pack-medicine.R")
testthat::test_dir("tests/testthat")test_file(path) runs one file, whereas
test_dir(path, filter = NULL) runs a directory of tests and
can select matching files with filter. Both accept a
reporter argument that controls how results are displayed,
but their defaults are normally suitable for interactive work.
Before a pull request, use devtools::check() as well. It
runs a broader R package check that includes the tests plus checks of
documentation, examples, dependencies, and package structure.
A failure means that observed behaviour did not match an expectation; it does not necessarily mean that the expectation is correct. Read the reported test description, file and line, then compare the actual and expected values. Ask:
Do not replace an expected value merely to silence a failure. First establish which behaviour the package is meant to guarantee.
For contributed room packs, a useful end-to-end test verifies that the rooms and named escapes are registered, confirms their order, starts a uniquely named test player, rejects at least one incorrect answer, and accepts every correct answer. This exercises the same exported functions that a learner uses and is why the example in Step 6 tests more than each room’s checker in isolation.