Skip to content

Commit 36de017

Browse files
committed
Merge commit '608decce7eb4bd198c0ff28dae0eb9b0c72e040d'
2 parents 86391c5 + 608decc commit 36de017

File tree

310 files changed

+11934
-3713
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

310 files changed

+11934
-3713
lines changed

.Rbuildignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,8 @@
2020
^\.github/workflows/R\.yaml$
2121
^\.github/workflows/pr-commands\.yaml$
2222
^CRAN-SUBMISSION$
23+
^[\.]?air\.toml$
24+
^\.vscode$
25+
^\.git-blame-ignore-rev$
26+
^CLAUDE\.md$
27+
^\.claude$

.claude/settings.local.json

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"$schema": "https://json.schemastore.org/claude-code-settings.json",
3+
"permissions": {
4+
"allow": [
5+
"Bash(find:*)",
6+
"Bash(R:*)",
7+
"Bash(rm:*)",
8+
"Bash(air format:*)",
9+
"Edit(R/**)",
10+
"Edit(tests/**)",
11+
"Edit(vignettes/**)"
12+
],
13+
"deny": []
14+
}
15+
}

.git-blame-ignore-rev

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# This file lists revisions of large-scale formatting/style changes so that
2+
# they can be excluded from git blame results.
3+
#
4+
# To set this file as the default ignore file for git blame, run:
5+
# $ git config blame.ignoreRevsFile .git-blame-ignore-revs
6+
7+
# https://github.com/r-lib/testthat/pull/2121
8+
13d17788e5d3a54fa83beed25e325703608f8b9f

.vscode/extensions.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"recommendations": [
3+
"Posit.air-vscode"
4+
]
5+
}

.vscode/settings.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"[r]": {
3+
"editor.formatOnSave": true,
4+
"editor.defaultFormatter": "Posit.air-vscode"
5+
}
6+
}

CLAUDE.md

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
# CLAUDE.md
2+
3+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4+
5+
## About This Project
6+
7+
testthat is R's most popular unit testing framework, used by thousands of CRAN packages. It provides functions to make testing R code as fun and addictive as possible, with clear expectations, visual progress indicators, and seamless integration with R package development workflows.
8+
9+
## Key Development Commands
10+
11+
General advice:
12+
* When running R from the console, always run it with `--quiet --vanilla`
13+
* Always run `air format .` after generating code
14+
15+
### Development tools
16+
17+
- `devtools::test()` - Run all tests
18+
- `devtools::test_file("tests/testthat/test-filename.R")` - Run tests in a specific file
19+
- DO NOT USE `devtools::test_active_file()`
20+
- `devtools::load_all()` - Load package for development
21+
- `devtools::check()` - Run R CMD check
22+
- `devtools::install()` - Install package locally
23+
24+
### Documentation
25+
26+
- Always run `devtools::document()` after changing any roxygen2 docs.
27+
28+
## Core Architecture
29+
30+
### Main Components
31+
32+
1. **Core Testing Functions** (`R/test-that.R`, `R/test-package.R`):
33+
- `test_that()` - The fundamental testing function
34+
- `test_local()`, `test_package()`, `test_check()` - Different ways to run test suites
35+
36+
2. **Expectations** (`R/expect-*.R`):
37+
- Modular expectation functions (equality, conditions, types, etc.)
38+
- Each expectation type has its own file following the pattern `expect-[type].R`
39+
40+
3. **Reporters** (`R/reporter*.R`):
41+
- Different output formats for test results
42+
- Object-oriented design with base `Reporter` class
43+
- Includes check, debug, progress, summary, JUnit, TAP formats
44+
45+
4. **Snapshot Testing** (`R/snapshot*.R`):
46+
- Value snapshots, file snapshots, output snapshots
47+
- Automatic management and comparison of expected outputs
48+
49+
5. **Parallel Testing** (`R/parallel*.R`):
50+
- Multi-core test execution
51+
- Configuration via `Config/testthat/parallel: true` in DESCRIPTION
52+
53+
6. **Mocking** (`R/mock*.R`, `R/mock2*.R`):
54+
- Function mocking capabilities
55+
- Both legacy (`mock.R`) and modern (`mock2*.R`) implementations
56+
57+
### Key Design Patterns
58+
59+
- **Editions**: testthat has different "editions" with varying behavior, controlled by `Config/testthat/edition`
60+
- **Reporters**: Extensible reporting system using R6 classes
61+
- **Lazy Evaluation**: Expectations use substitute() and lazy evaluation for better error messages
62+
- **C++ Integration**: Core functionality implemented in C++ for performance
63+
64+
### File Organization
65+
66+
- `R/` - All R source code, organized by functionality
67+
- `src/` - C++ source code and Makevars
68+
- `inst/include/testthat/` - C++ headers for other packages to use
69+
- `tests/testthat/` - Package's own comprehensive test suite
70+
- `vignettes/` - Documentation on testing concepts and workflows
71+
72+
### Important Configuration
73+
74+
The package uses several DESCRIPTION fields for configuration:
75+
- `Config/testthat/edition: 3` - Sets testthat edition
76+
- `Config/testthat/parallel: true` - Enables parallel testing
77+
- `Config/testthat/start-first` - Tests to run first in parallel mode
78+
79+
### C++ Testing Infrastructure
80+
81+
testthat provides C++ testing capabilities via Catch framework:
82+
- Headers in `inst/include/testthat/`
83+
- Test runner infrastructure in `src/test-runner.cpp`
84+
- Integration with R's testing system
85+
86+
### Snapshot Testing Workflow
87+
88+
- Snapshots stored in `tests/testthat/_snaps/`
89+
- Different snapshot types: values, files, output
90+
- Version-specific snapshots for different R versions
91+
- Use `testthat::snapshot_accept()` to update snapshots
92+
93+
This codebase prioritizes backward compatibility, comprehensive testing, and clear, descriptive error messages to help R developers write better tests.

DESCRIPTION

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -17,23 +17,23 @@ BugReports: https://github.com/r-lib/testthat/issues
1717
Depends:
1818
R (>= 4.1.0)
1919
Imports:
20-
brio (>= 1.1.3),
21-
callr (>= 3.7.3),
22-
cli (>= 3.6.1),
23-
desc (>= 1.4.2),
24-
evaluate (>= 1.0.1),
25-
jsonlite (>= 1.8.7),
26-
lifecycle (>= 1.0.3),
20+
brio (>= 1.1.5),
21+
callr (>= 3.7.6),
22+
cli (>= 3.6.5),
23+
desc (>= 1.4.3),
24+
evaluate (>= 1.0.4),
25+
jsonlite (>= 2.0.0),
26+
lifecycle (>= 1.0.4),
2727
magrittr (>= 2.0.3),
2828
methods,
29-
pkgload (>= 1.3.2.1),
29+
pkgload (>= 1.4.0),
3030
praise (>= 1.0.0),
31-
processx (>= 3.8.2),
32-
ps (>= 1.7.5),
33-
R6 (>= 2.5.1),
34-
rlang (>= 1.1.1),
31+
processx (>= 3.8.6),
32+
ps (>= 1.9.1),
33+
R6 (>= 2.6.1),
34+
rlang (>= 1.1.6),
3535
utils,
36-
waldo (>= 0.6.0),
36+
waldo (>= 0.6.2),
3737
withr (>= 3.0.2)
3838
Suggests:
3939
covr,

NAMESPACE

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ export(ProgressReporter)
4545
export(RStudioReporter)
4646
export(Reporter)
4747
export(SilentReporter)
48+
export(SlowReporter)
4849
export(StopReporter)
4950
export(SummaryReporter)
5051
export(TapReporter)
@@ -114,6 +115,7 @@ export(expect_no_warning)
114115
export(expect_null)
115116
export(expect_output)
116117
export(expect_output_file)
118+
export(expect_r6_class)
117119
export(expect_reference)
118120
export(expect_s3_class)
119121
export(expect_s4_class)
@@ -145,29 +147,27 @@ export(is.expectation)
145147
export(is_a)
146148
export(is_checking)
147149
export(is_equivalent_to)
148-
export(is_false)
149150
export(is_identical_to)
150151
export(is_informative_error)
151152
export(is_less_than)
152153
export(is_more_than)
153-
export(is_null)
154154
export(is_parallel)
155155
export(is_snapshot)
156156
export(is_testing)
157-
export(is_true)
158157
export(it)
159158
export(local_edition)
160159
export(local_mock)
161160
export(local_mocked_bindings)
161+
export(local_on_cran)
162162
export(local_reproducible_output)
163163
export(local_snapshotter)
164164
export(local_test_context)
165165
export(local_test_directory)
166166
export(make_expectation)
167-
export(matches)
168167
export(mock_output_sequence)
169168
export(new_expectation)
170169
export(not)
170+
export(pass)
171171
export(prints_text)
172172
export(quasi_label)
173173
export(run_cpp_tests)
@@ -192,6 +192,7 @@ export(skip_on_os)
192192
export(skip_on_travis)
193193
export(skip_unless_r)
194194
export(snapshot_accept)
195+
export(snapshot_reject)
195196
export(snapshot_review)
196197
export(source_dir)
197198
export(source_file)
@@ -230,5 +231,6 @@ import(rlang)
230231
importFrom(R6,R6Class)
231232
importFrom(brio,readLines)
232233
importFrom(brio,writeLines)
234+
importFrom(lifecycle,deprecated)
233235
importFrom(magrittr,"%>%")
234236
useDynLib(testthat, .registration = TRUE)

NEWS.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,41 @@
11
# testthat (development version)
22

3+
* `test_dir()`, `test_file()`, `test_package()`, `test_check()`, `test_local()`, `source_file()` gain a `shuffle` argument uses `sample()` to randomly reorder the top-level expressions in each test file (#1942). This random reordering surfaces dependencies between tests and code outside of any test, as well as dependencies between tests. This helps you find and eliminate unintentional dependencies.
4+
* `snapshot_accept(test)` now works when the test file name contains `.` (#1669).
5+
* `local_mock()` and `with_mock()` have been deprecated because they are no longer permitted in R 4.5.
6+
* `snapshot_review()` now passes `...` on to `shiny::runApp()` (#1928).
7+
* `expect_named()` now gives more informative errors (#2091).
8+
* `expect_*()` functions consistently and rigorously check their inputs (#1754).
9+
* `test_that()` no longer warns about the absence of `{}` since it no longer seems to be necessary.
10+
* `test_that()`, `describe()`, and `it()` can now be arbitrarily nested. Each component will skip only if it and its subtests don't contain any expectations. The interactive stop reporter has been fixed so it doesn't duplicate failures. (#2063, #2188).
11+
* Test filtering now works with `it()`, and the `desc` argument can take a character vector in order to recursively filter subtests (i.e. `it()` nested inside of `describe()`) (#2118).
12+
* New `snapshot_reject()` rejects all modified snapshots by deleting the `.new` variants (#1923).
13+
* New `SlowReporter` makes it easier to find the slowest tests in your package. The easiest way to run it is with `devtools::test(reporter = "slow")` (#1466).
14+
* Power `expect_mapequal()` with `waldo::compare(list_as_map = TRUE)` (#1521).
15+
* On CRAN, `test_that()` now automatically skips if a package is not installed (#1585). Practically, this means that you no longer need to check that suggested packages are installed. (We don't do this in the tidyverse because we think it has limited payoff, but other styles advise differently.)
16+
* `expect_snapshot()` no longer skips on CRAN, as that skips the rest of the test. Instead it just returns, neither succeeding nor failing (#1585).
17+
* Interrupting a test now prints the test name. This makes it easier to tell where a very slow test might be hanging (#1464)
18+
* Parallel testthat now does not ignore test files with syntax errors (#1360).
19+
* `expect_lt()`, `expect_gt()`, and friends have a refined display that is more likely to display the correct number of digits and shows you the actual values compared.
20+
* `describe()`, `it()`, and `test_that()` now have a shared stack of descriptions so that if you nest any inside of each other, any resulting failures will show you the full path.
21+
* `describe()` now correctly scopes `skip()` (#2007).
22+
* `ParallelProgressReporter` now respect `max_failures` (#1162).
23+
* The last snapshot is no longer lost if the snapshot file is missing the final newline (#2092). It's easy to accidentally remove this because there are two trailing new lines in snapshot files and many editors will automatically remove if you touch the file.
24+
* New `expect_r6_class()` (#2030).
25+
* `expect_*()` functions consistently and rigorously check their inputs (#1754).
26+
* `JunitReporter()` no longer fails with `"no applicable method for xml_add_child"` for warnings outside of tests (#1913). Additionally, warnings now save their backtraces.
27+
* `JunitReporter()` strips ANSI escapes in more placese (#1852, #2032).
28+
* `try_again()` is now publicised. The first argument is now the number of retries, not tries (#2050).
29+
* `vignette("custom-expectations)` has been overhauled to make it much clearer how to create high-quality expectations (#2113, #2132, #2072).
30+
* `expect_snapshot()` and friends will now fail when creating a new snapshot on CI. This is usually a signal that you've forgotten to run it locally before committing (#1461).
31+
* `expect_snapshot_value()` can now handle expressions that generate `-` (#1678) or zero length atomic vectors (#2042).
32+
* `expect_matches()` failures should be a little easier to read (#2135, #2181).
33+
* New `local_on_cran(TRUE)` allows you to simulate how your tests will run on CRAN (#2112).
34+
* `expect_no_*()` now executes the entire code block, rather than stopping at the first message or warning (#1991).
35+
* `expect_no_failures()` and `expect_no_successes()` are now deprecated as `expect_success()` now test for no failures and `expect_failure()` tests for no successes (#)
36+
* New `pass()` function to use in place of `succeed()` (#2113).
37+
* `expectation()` is now a combination of `new_expectation()` and `exp_signal()` (#2125).
38+
* `is_null()`/`matches()` deprecated in 2.0.0 (2017-12-19) and `is_true()`/`is_false()` deprecated in 2.1.0 (2019-04-23) have been removed (#2109).
339
* `local_edition()` now gives a useful error for bad values (#1547).
440
* testthat now requires R 4.1.
541
* `expect_s4_class()` now supports unquoting (@stibu81, #2064).
@@ -9,6 +45,7 @@
945
* `skip_on_os()` gains an option `"emscripten"` of the `os` argument to skip tests on Emscripten (@eitsupi, #2103).
1046
* New expectation, `expect_shape()`, for testing the shape (i.e., the `length()`, `nrow()`, `ncol()`, or `dim()`), all in one place (#1423, @michaelchirico).
1147
* `expect_snapshot_file(name=)` must have a unique file path. If a snapshot file attempts to be saved with a duplicate `name`, an error will be thrown. (#1592)
48+
* New expectation, `expect_shape()`, for testing the shape (i.e., the `nrow()`, `ncol()`, or `dim()`), all in one place (#1423, @michaelchirico).
1249

1350
# testthat 3.2.3
1451

R/auto-test.R

Lines changed: 30 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,18 @@
11
#' Watches code and tests for changes, rerunning tests as appropriate.
22
#'
3+
#' @description
4+
#' `r lifecycle::badge("superseded")`
5+
#'
36
#' The idea behind `auto_test()` is that you just leave it running while
47
#' you develop your code. Every time you save a file it will be automatically
58
#' tested and you can easily see if your changes have caused any test
6-
#' failures.
9+
#' failures.
710
#'
811
#' The current strategy for rerunning tests is as follows:
912
#'
1013
#' - if any code has changed, then those files are reloaded and all tests
1114
#' rerun
1215
#' - otherwise, each new or modified test is run
13-
#'
14-
#' In the future, `auto_test()` might implement one of the following more
15-
#' intelligent alternatives:
16-
#'
17-
#' - Use codetools to build up dependency tree and then rerun tests only
18-
#' when a dependency changes.
19-
#' - Mimic ruby's autotest and rerun only failing tests until they pass,
20-
#' and then rerun all tests.
21-
#
2216
#' @seealso [auto_test_package()]
2317
#' @export
2418
#' @param code_path path to directory containing code
@@ -27,10 +21,14 @@
2721
#' @param env environment in which to execute test suite.
2822
#' @param hash Passed on to [watch()]. When FALSE, uses less accurate
2923
#' modification time stamps, but those are faster for large files.
30-
#' @keywords debugging
31-
auto_test <- function(code_path, test_path, reporter = default_reporter(),
32-
env = test_env(),
33-
hash = TRUE) {
24+
#' @keywords internal
25+
auto_test <- function(
26+
code_path,
27+
test_path,
28+
reporter = default_reporter(),
29+
env = test_env(),
30+
hash = TRUE
31+
) {
3432
reporter <- find_reporter(reporter)
3533
code_path <- normalizePath(code_path)
3634
test_path <- normalizePath(test_path)
@@ -63,16 +61,17 @@ auto_test <- function(code_path, test_path, reporter = default_reporter(),
6361
watch(c(code_path, test_path), watcher, hash = hash)
6462
}
6563

66-
#' Watches a package for changes, rerunning tests as appropriate.
67-
#'
6864
#' @param pkg path to package
6965
#' @export
7066
#' @param reporter test reporter to use
7167
#' @param hash Passed on to [watch()]. When FALSE, uses less accurate
7268
#' modification time stamps, but those are faster for large files.
73-
#' @keywords debugging
74-
#' @seealso [auto_test()] for details on how method works
75-
auto_test_package <- function(pkg = ".", reporter = default_reporter(), hash = TRUE) {
69+
#' @rdname auto_test
70+
auto_test_package <- function(
71+
pkg = ".",
72+
reporter = default_reporter(),
73+
hash = TRUE
74+
) {
7675
reporter <- find_reporter(reporter)
7776

7877
path <- pkgload::pkg_path(pkg)
@@ -86,7 +85,12 @@ auto_test_package <- function(pkg = ".", reporter = default_reporter(), hash = T
8685
# Start by loading all code and running all tests
8786
withr::local_envvar("NOT_CRAN" = "true")
8887
pkgload::load_all(path)
89-
test_dir(test_path, package = package, reporter = reporter$clone(deep = TRUE), stop_on_failure = FALSE)
88+
test_dir(
89+
test_path,
90+
package = package,
91+
reporter = reporter$clone(deep = TRUE),
92+
stop_on_failure = FALSE
93+
)
9094

9195
# Next set up watcher to monitor changes
9296
watcher <- function(added, deleted, modified) {
@@ -106,7 +110,11 @@ auto_test_package <- function(pkg = ".", reporter = default_reporter(), hash = T
106110
cat("Changed code: ", paste0(basename(code), collapse = ", "), "\n")
107111
cat("Rerunning all tests\n")
108112
pkgload::load_all(path, quiet = TRUE)
109-
test_dir(test_path, package = package, reporter = reporter$clone(deep = TRUE))
113+
test_dir(
114+
test_path,
115+
package = package,
116+
reporter = reporter$clone(deep = TRUE)
117+
)
110118
} else if (length(tests) > 0) {
111119
# If test changes, rerun just that test
112120
cat("Rerunning tests: ", paste0(basename(tests), collapse = ", "), "\n")
@@ -115,7 +123,7 @@ auto_test_package <- function(pkg = ".", reporter = default_reporter(), hash = T
115123
test_dir = test_path,
116124
test_package = package,
117125
test_paths = tests,
118-
env = env,
126+
env = env,
119127
reporter = reporter$clone(deep = TRUE)
120128
)
121129
}

0 commit comments

Comments
 (0)