Engineering a Reliable R Package for Regulatory Use Using “rpact” as an Example

useR! 2024, Salzburg, Austria

Friedrich Pahlke and Gernot Wassmer

RPACT

July 10, 2024

Automation

Automation of recurring validation processes/activities

library(rpact.validator)

createNewsTexFile()
createBaseRPackagesTable()
createOutputSpecification()
createPlotTypeSpecification()
createSourceDirectoryStructure()
createBusinessObjectStructure()
createUnitTests()
createTestPlan()
createUnitTestResultDocumentation()
createPerformanceQualification()
runSopCheck()

\(\rightarrow\) Documentation work

Automation of recurring validation processes/activities

library(rpact.validator)

createNewsTexFile()
createBaseRPackagesTable()
createOutputSpecification()
createPlotTypeSpecification()
createSourceDirectoryStructure()
createBusinessObjectStructure()
createUnitTests()
createTestPlan()
createUnitTestResultDocumentation()
createPerformanceQualification()
runSopCheck()

\(\rightarrow\) Quality control of the process steps up to the final validation documentation

\(\rightarrow\) The SOPs help not to forget or overlook any step

Automation of recurring validation processes/activities

library(rpact.validator)

createNewsTexFile()
createBaseRPackagesTable()
createOutputSpecification()
createPlotTypeSpecification()
createSourceDirectoryStructure()
createBusinessObjectStructure()
createUnitTests()
createTestPlan()
createUnitTestResultDocumentation()
createPerformanceQualification()
runSopCheck()

\(\rightarrow\) Efficient unit test case generation

Example: Test Template

test_template_f_design_group_sequential.R:

#' @exit Do not create the unit tests again
#' @context Testing the Group Sequential and Inverse Normal Design Functionality

#' @test_that 'getDesignInverseNormal' with default parameters: 
#'             parameters and results are as expected

#' @refFS[Tab.]{fs:tab:output:getDesignInverseNormal}
#' @refFS[Formula]{fs:criticalValuesOBrienFleming}
x0 <- getDesignInverseNormal()
getUnitTestObject(x0, "x0")

Template

#' @exit Do not create the unit tests again
#' @context Testing the Group Sequential and Inverse Normal Design Functionality

#' @test_that 'getDesignInverseNormal' with default parameters: 
#'             parameters and results are as expected

#' @refFS[Tab.]{fs:tab:output:getDesignInverseNormal}
#' @refFS[Formula]{fs:criticalValuesOBrienFleming}
x0 <- getDesignInverseNormal()
getUnitTestObject(x0, "x0")

Regeneration is disabled

#' @exit Do not create the unit tests again
#' @context Testing the Group Sequential and Inverse Normal Design Functionality

#' @test_that 'getDesignInverseNormal' with default parameters: 
#'             parameters and results are as expected

#' @refFS[Tab.]{fs:tab:output:getDesignInverseNormal}
#' @refFS[Formula]{fs:criticalValuesOBrienFleming}
x0 <- getDesignInverseNormal()
getUnitTestObject(x0, "x0")

Section title in the document (context for testthat version <3)

#' @exit Do not create the unit tests again
#' @context Testing the Group Sequential and Inverse Normal Design Functionality

#' @test_that 'getDesignInverseNormal' with default parameters: 
#'             parameters and results are as expected

#' @refFS[Tab.]{fs:tab:output:getDesignInverseNormal}
#' @refFS[Formula]{fs:criticalValuesOBrienFleming}
x0 <- getDesignInverseNormal()
getUnitTestObject(x0, "x0")

Unit test title/description

#' @exit Do not create the unit tests again
#' @context Testing the Group Sequential and Inverse Normal Design Functionality

#' @test_that 'getDesignInverseNormal' with default parameters: 
#'             parameters and results are as expected

#' @refFS[Tab.]{fs:tab:output:getDesignInverseNormal}
#' @refFS[Formula]{fs:criticalValuesOBrienFleming}
x0 <- getDesignInverseNormal()
getUnitTestObject(x0, "x0")

Reference to a table in the Functional Specification

#' @exit Do not create the unit tests again
#' @context Testing the Group Sequential and Inverse Normal Design Functionality

#' @test_that 'getDesignInverseNormal' with default parameters: 
#'             parameters and results are as expected

#' @refFS[Tab.]{fs:tab:output:getDesignInverseNormal}
#' @refFS[Formula]{fs:criticalValuesOBrienFleming}
x0 <- getDesignInverseNormal()
getUnitTestObject(x0, "x0")

Reference to a formula in the Functional Specification

#' @exit Do not create the unit tests again
#' @context Testing the Group Sequential and Inverse Normal Design Functionality

#' @test_that 'getDesignInverseNormal' with default parameters: 
#'             parameters and results are as expected

#' @refFS[Tab.]{fs:tab:output:getDesignInverseNormal}
#' @refFS[Formula]{fs:criticalValuesOBrienFleming}
x0 <- getDesignInverseNormal()
getUnitTestObject(x0, "x0")

The function call to be tested

#' @exit Do not create the unit tests again
#' @context Testing the Group Sequential and Inverse Normal Design Functionality

#' @test_that 'getDesignInverseNormal' with default parameters: 
#'             parameters and results are as expected

#' @refFS[Tab.]{fs:tab:output:getDesignInverseNormal}
#' @refFS[Formula]{fs:criticalValuesOBrienFleming}
x0 <- getDesignInverseNormal()
getUnitTestObject(x0, "x0")

Create a unit test for each field of the object x0.

Example: Test Template

library(rpact)
library(rpact.validator)
x0 <- getDesignInverseNormal()
getUnitTestObject(x0, "x0")

results in:

## Comparison of the results of TrialDesignInverseNormal 
## object 'x0' with expected results
expect_equal(x0$alphaSpent, c(0.00025917372, 0.0071600594, 0.02499999), 
    tolerance = 1e-07)
expect_equal(x0$criticalValues, c(3.4710914, 2.4544323, 2.0040356), 
    tolerance = 1e-07)
expect_equal(x0$stageLevels, c(0.00025917372, 0.0070553616, 0.022533125), 
    tolerance = 1e-07)

invisible(capture.output(expect_error(print(x0), NA)))
expect_output(print(x0)$show())
invisible(capture.output(expect_error(summary(x0), NA)))
expect_output(summary(x0)$show())
x0CodeBased <- eval(parse(text = 
    getObjectRCode(x0, stringWrapParagraphWidth = NULL)))
expect_equal(x0CodeBased$alphaSpent, x0$alphaSpent, tolerance = 1e-07)
expect_equal(x0CodeBased$criticalValues, x0$criticalValues, tolerance = 1e-07)
expect_equal(x0CodeBased$stageLevels, x0$stageLevels, tolerance = 1e-07)
expect_type(names(x0), "character")
df <- as.data.frame(x0)
expect_s3_class(df, "data.frame")
expect_true(nrow(df) > 0 && ncol(df) > 0)
mtx <- as.matrix(x0)
expect_true(is.matrix(mtx))
expect_true(nrow(mtx) > 0 && ncol(mtx) > 0)

Contact

Friedrich Pahlke