It is important to test software.
One approach is unit-testing, and for R
packages this can e.g. be done using testthat
.
It is also important to document software. For R
packages roxygen2
is really helpful:
It enables you to write documentation in the code file in the R/
folder
where the function is
implemented. And then roxygen2
takes care of handling the Rd
files in the man/
folder.
I have made a new R
package that combines these approaches:
roxytest
.
The idea is to write tests in the documentation near the implementing code,
and then roxytest
takes care of handling tests/testthat/*.R
files just
as roxygen2
handles the man/*.Rd
files.
roxytest
is still experimental, but please have a look at https://github.com/mikldk/roxytest.
There is also a demonstration package that shows how to use it at https://github.com/mikldk/roxytest-demo.
Not much is needed to use roxytest
:
- Install it:
devtools::install_github('mikldk/roxytest')
- Specify in your package’s
DESCRIPTION
file that you will use it: see below - Use it: see below
- Run
roxygen2
to generate the documentation andtestthat
tests:roxygen2::roxygenise()
The DESCRIPTION
file
Add the following lines to your package’s DESCRIPTION
file:
(Or make appropriate changes to obtain similar results.) The reason that roxytest is in Depends
rather than in Imports
is to attach the package immediately so that roxygen2
can find the roclet.
Imports:
roxygen2,
testthat
Depends:
R (>= 2.10),
roxytest
Roxygen: list(roclets = c("namespace", "rd", "roxytest::testthat_roclet"))
Use it
For example, if the file R/functions.R
contains this code (from roxytest-demo):
#' A function to do x
#'
#' @param x A number
#'
#' @tests
#' expect_equal(foo(2), sqrt(2))
#' expect_error(foo("a string"))
#'
#' @return something
foo <- function(x) {
return(sqrt(x))
}
#' A function to do y
#'
#' @param x Character vector
#' @param y Character vector
#'
#' @tests
#' expect_equal(bar("A", "B"), paste("A", "B", sep = "/"))
#'
#' @export
bar <- function(x, y) {
paste0(x, "/", y)
}
Then roxygen2::roxygenise()
will generate (with the roxytest::testthat_roclet
roclet)
the file tests/testthat/test-roxytest-functions.R
with this content:
# Generated by roxytest: Do not edit by hand!
context("File R/functions.R")
test_that("Function foo()", {
expect_equal(foo(2), sqrt(2))
expect_error(foo("a string"))
})
test_that("Function bar()", {
expect_equal(bar("A", "B"), paste("A", "B", sep = "/"))
})
Wish-list
In order to make the usage a bit smoother, I have a small wish-list that I am working on, but
it requires changes to roxygen2
and Rstudio:
- Rstudio: CTRL+SHIFT+D would run
roxygen2::roxygenise()
instead ofdevtools::document(roclets=c('rd', 'collate', 'namespace'))
- Project options -> Build tools -> If all check marks are removed, nothing happens with CTRL+SHIFT+D. If instead
devtools::document()
would be ran it would work. - Feature request submitted
- Project options -> Build tools -> If all check marks are removed, nothing happens with CTRL+SHIFT+D. If instead
roxygen2
:- In
DESCRIPTION
,Roxygen: list(roclets = c("namespace", "rd", "roxytest::testthat_roclet"))
must be added. It would be more consistent to omit_roclet
. - Easier test;
roxygen2
usesroxygen2::roc_proc_text
; it would be nice to be able to use multiple roclets - Both addressed in issue 891 for
roxygen2
- In