R package dev with devtools, testthat, and roxygen2.
R package development
Key commands
# Run code in the package
Rscript -e "devtools::load_all(); code"
# Run all tests
Rscript -e "devtools::test()"
# Run all tests for files starting with {name}
Rscript -e "devtools::test(filter = '^{name}')"
# Run all tests for R/{name}.R
Rscript -e "devtools::test_active_file('R/{name}.R')"
# Run a single test "blah" for R/{name}.R
Rscript -e "devtools::test_active_file('R/{name}.R', desc = 'blah')"
# Redocument the package
Rscript -e "devtools::document()"
# Check pkgdown documentation
Rscript -e "pkgdown::check_pkgdown()"
# Check the package with R CMD check
Rscript -e "devtools::check()"
# Format code
air format .
Coding
- Always run
air format .after generating code. - Use the base pipe operator (
|>) not the magrittr pipe (%>%). - Use
\() ...for single-line anonymous functions. For all other cases, usefunction() {...}.
Testing
For the full testthat API (fixtures, mocking, snapshots, BDD-style tests), see testing-r-packages. Match the package’s existing expect_error()/expect_warning() or snapshot-testing style where precedent exists. On a fresh package with no precedent, follow testing-r-packages’ preference for snapshot-based error/warning tests.
- Tests for
R/{name}.Rgo intests/testthat/test-{name}.R. - All new code should have an accompanying test.
- If there are existing tests, place new tests next to similar existing tests.
- Strive to keep tests minimal with few comments.
- Avoid
expect_true()andexpect_false()in favour of a specific expectation which will give a better failure message. - When testing errors and warnings, match the package’s existing precedent where one exists (see above). On a fresh package with no precedent, use
expect_snapshot(error = TRUE)for errors andexpect_snapshot()for warnings, notexpect_error()orexpect_warning(). A snapshot lets the user review the full text of the output.
Documentation
- Every user-facing function should be exported and have roxygen2 documentation.
- Wrap roxygen comments at 80 characters.
- Internal functions should not have roxygen documentation.
- Whenever you add a new (non-internal) documentation topic, also add the topic to
_pkgdown.yml. - Always re-document the package after changing a roxygen2 comment.
- Use
pkgdown::check_pkgdown()to check that all topics are included in the reference index.
NEWS.md
This is the tidyverse-style default. When the package already has an established NEWS.md style, match that existing style instead — see r-pkg-news, whose whole job is reading a package’s precedent before drafting a new entry.
- Every user-facing change should be given a bullet in
NEWS.md. Do not add bullets for small documentation changes or internal refactorings. - Each bullet should briefly describe the change to the end user.
- If the change is related to a function, put the name of the function early in the bullet.
- If the bullet is related to a GitHub issue or pull request, reference it by number in parentheses before the final period:
(#123).. - Order bullets alphabetically by function name. Put all bullets that don’t mention function names at the beginning.
Relationship to other skills
This skill is a day-to-day dev-loop reference (load, test, document, format) with this author’s own conventions. It does not replace, and does not cover:
r-pkg-check— running and triagingdevtools::check()output.r-pkg-cran-checklist— the CRAN release mechanics (win-builder, revdep checks,devtools::release()).cran-extrachecks— the ad-hoc/stylistic CRAN checklist (Title case,@returntags, URL hygiene) thatdevtools::check()doesn’t catch on its own.r-pkg-news— matching a package’s existingNEWS.mdstyle, which overrides this skill’s own default above.testing-r-packages— the full testthat API (fixtures, mocking, snapshots, BDD-style tests). Match the package’s existingexpect_error()/expect_warning()or snapshot-testing style where precedent exists. On a fresh package with no precedent, follow testing-r-packages’ preference for snapshot-based error/warning tests, which agrees with this skill’s own default above.