8 Running test cases

Using package wyz.code.offensiveProgramming, you have the opportunity to define test cases and to embed them in your class definition, to ease retrieval and reuse.

8.1 Reusing defined test case definitions

Doing so, allows to get following benefits

  1. discover defined test case definitions
  2. run of any test case definition
  3. get interactively the R code of a test case, allowing you to play with it, manually, when needed
  4. get contextual results from the test case runs

8.2 Embedding test cases in class definition

This is accomplished easily. You just have to declare a variable named testCaseDefinitions, and provide its content, that is a data.table. Content could be partial or complete depending of your goals. Spectrum of provided tests cases is as you desire it to be, as shallow or deep as needed.

The data.table must hold following columns and content

  1. function_name, a vector of strings, each being the name of the function to test,
  2. standard_evaluation, a vector of strings, where values are taken from set {‘correct’, ‘erroneous’, ‘failure’}
  3. type_checking_enforcement, , a vector of strings, where values are taken from set {‘correct’, ‘erroneous’, ‘failure’}
  4. test case definitions, that is a list of TestCaseDefinition objects.

Correct implies right type and right result. Erroneous implies right type and wrong result. Failure implies wrong type. To get more details about syntax, please refer to manual page of Definitions.

8.2.2 A more complex case

AdditionTCFIG1
#> function () 
#> {
#>     self <- environment()
#>     class(self) <- append("AdditionTCFIG1", class(self))
#>     addDouble <- function(x_d, y_d) x_d + y_d
#>     addInteger <- function(x_i, y_i) x_i + y_i
#>     addMultiDouble <- function(...) as.double(sum(..., na.rm = TRUE))
#>     addMultiInteger <- function(x_i, ...) x_i + sum(..., na.rm = TRUE)
#>     divideByZero <- function(x_n) x_n/0
#>     generateWarning <- function(x_) 1:3 + 1:7
#>     generateError <- function() stop("generated error")
#>     function_return_types <- data.table(function_name = c("addDouble", 
#>         "addInteger", "addMultiDouble", "divideByZero", "addMultiInteger", 
#>         "generateWarning", "generateError"), return_value = c("x_d", 
#>         "x_i", "x_d", "x_d", "x_i", "x_w", "x_er"))
#>     test_case_definitions <- data.table(function_name = c(rep("addDouble", 
#>         9), rep("addInteger", 9), rep("divideByZero", 3), "generateWarning", 
#>         "generateError", rep("addMultiDouble", 3), rep("addMultiInteger", 
#>             3)), standard_evaluation = c(rep("correct", 5), "erroneous", 
#>         rep("correct", 7), "erroneous", rep("correct", 8), "failure", 
#>         rep("correct", 6)), type_checking_enforcement = c(rep("correct", 
#>         5), "erroneous", rep("failure", 3), rep("correct", 4), 
#>         rep("failure", 5), rep("correct", 3), "correct", "failure", 
#>         "correct", "correct", "correct", "correct", "failure", 
#>         "correct"), test_case = list(TestCaseDefinition(list(as.double(34L), 
#>         44.5), 78.5, "sum 2 doubles"), TestCaseDefinition(list(34, 
#>         NA_real_), NA_real_, "sum 1 double and 1 NA_real_"), 
#>         TestCaseDefinition(list(NA_real_, NA_real_), NA_real_, 
#>             "sum 2 NA_real_"), TestCaseDefinition(list(NaN, NaN), 
#>             NaN, "sum 2 NAN"), TestCaseDefinition(list(Inf, Inf), 
#>             Inf, "sum 2 Inf"), TestCaseDefinition(list(as.integer(34.7), 
#>             as.integer(44.9)), 80, "sum 2 as.integers confused with sum of rounded value as expectation"), 
#>         TestCaseDefinition(list(34L, 44.5), 78.5, "sum of 1 integer and 1 double"), 
#>         TestCaseDefinition(list(34, NA_integer_), NA_real_, "sum of 1 integer and 1 double"), 
#>         TestCaseDefinition(list(NA, NA), NA, "sum 2 NA"), TestCaseDefinition(list(34L, 
#>             as.integer(44.5)), 78L, "sum 2 integers"), TestCaseDefinition(list(34L, 
#>             NA_integer_), NA_integer_, "sum 1 integer and 1 NA_integer"), 
#>         TestCaseDefinition(list(NA_integer_, NA_integer_), NA_integer_, 
#>             "sum 2 NA_integer"), TestCaseDefinition(list(as.integer("45.654"), 
#>             44L), 89L, "sum a converted string with one integer"), 
#>         TestCaseDefinition(list(34L, 44.5), 78L, "sum 1 integer and 1 double"), 
#>         TestCaseDefinition(list(34L, Inf), Inf, "sum 1 integer and 1 Inf"), 
#>         TestCaseDefinition(list(34L, NaN), NaN, "sum 1 integer and 1 NAN"), 
#>         TestCaseDefinition(list(34L, NA), NA, "sum 1 integer and 1 NA"), 
#>         TestCaseDefinition(list(c(34L, 35L), 44L), c(78L, 79L), 
#>             "sum a vector of 2 integers with 1 integer"), TestCaseDefinition(list(1), 
#>             Inf, "1 / 0"), TestCaseDefinition(list(-1), -Inf, 
#>             "-1 / 0"), TestCaseDefinition(list(0), NaN, "0 / 0"), 
#>         TestCaseDefinition(list(0), 1:3 + 1:7, "generate warning"), 
#>         TestCaseDefinition(list(), NA, "generate error"), TestCaseDefinition(list(34L, 
#>             44.5), 78.5, "sum of 1 integer and 1 double"), TestCaseDefinition(list(34, 
#>             35L, 36L, NA_integer_), 105, "sum of 1 double, 2 integers and 1 NA_integer_"), 
#>         TestCaseDefinition(list(), 0, "sum of nothing"), TestCaseDefinition(list(34L, 
#>             44L, 1L, 1L), 80L, "sum of 4 integers"), TestCaseDefinition(list(34L, 
#>             35, 36, NA_integer_), 105, "sum of 1 integer, 2 doubles and 1 NA_integer_"), 
#>         TestCaseDefinition(list(34L), 34L, "sum of one integer and nothing")))
#>     label <- "erroneous class instrumentation: test cases uses function divideByZero that is not instrumented for type checking enforcement"
#>     self
#> }
#> <bytecode: 0x000000001b062cd0>

Much more complete instrumentation with 29 test cases, various expected outputs, varying from evaluation model to consider.

8.3 Test case definitions verification

To verify test cases definitions you may use the low level function verifyTestCaseDefinitions or the higher level one named retrieveTestCaseDefinitions.

retrieveTestCaseDefinitions(AdditionTCFIG1())
#>       function_name standard_evaluation type_checking_enforcement
#>  1:       addDouble             correct                   correct
#>  2:       addDouble             correct                   correct
#>  3:       addDouble             correct                   correct
#>  4:       addDouble             correct                   correct
#>  5:       addDouble             correct                   correct
#>  6:       addDouble           erroneous                 erroneous
#>  7:       addDouble             correct                   failure
#>  8:       addDouble             correct                   failure
#>  9:       addDouble             correct                   failure
#> 10:      addInteger             correct                   correct
#> 11:      addInteger             correct                   correct
#> 12:      addInteger             correct                   correct
#> 13:      addInteger             correct                   correct
#> 14:      addInteger           erroneous                   failure
#> 15:      addInteger             correct                   failure
#> 16:      addInteger             correct                   failure
#> 17:      addInteger             correct                   failure
#> 18:      addInteger             correct                   failure
#> 19:    divideByZero             correct                   correct
#> 20:    divideByZero             correct                   correct
#> 21:    divideByZero             correct                   correct
#> 22: generateWarning             correct                   correct
#> 23:   generateError             failure                   failure
#> 24:  addMultiDouble             correct                   correct
#> 25:  addMultiDouble             correct                   correct
#> 26:  addMultiDouble             correct                   correct
#> 27: addMultiInteger             correct                   correct
#> 28: addMultiInteger             correct                   failure
#> 29: addMultiInteger             correct                   correct
#>       function_name standard_evaluation type_checking_enforcement
#>                test_case
#>  1: <TestCaseDefinition>
#>  2: <TestCaseDefinition>
#>  3: <TestCaseDefinition>
#>  4: <TestCaseDefinition>
#>  5: <TestCaseDefinition>
#>  6: <TestCaseDefinition>
#>  7: <TestCaseDefinition>
#>  8: <TestCaseDefinition>
#>  9: <TestCaseDefinition>
#> 10: <TestCaseDefinition>
#> 11: <TestCaseDefinition>
#> 12: <TestCaseDefinition>
#> 13: <TestCaseDefinition>
#> 14: <TestCaseDefinition>
#> 15: <TestCaseDefinition>
#> 16: <TestCaseDefinition>
#> 17: <TestCaseDefinition>
#> 18: <TestCaseDefinition>
#> 19: <TestCaseDefinition>
#> 20: <TestCaseDefinition>
#> 21: <TestCaseDefinition>
#> 22: <TestCaseDefinition>
#> 23: <TestCaseDefinition>
#> 24: <TestCaseDefinition>
#> 25: <TestCaseDefinition>
#> 26: <TestCaseDefinition>
#> 27: <TestCaseDefinition>
#> 28: <TestCaseDefinition>
#> 29: <TestCaseDefinition>
#>                test_case

8.4 Discovering test cases descriptions

Any R object instrumented with test case definitions allows for defined test case definitions discovery.

retrieveTestCaseDescriptions(AdditionTCFIG1())
#>       function_name
#>  1:       addDouble
#>  2:       addDouble
#>  3:       addDouble
#>  4:       addDouble
#>  5:       addDouble
#>  6:       addDouble
#>  7:       addDouble
#>  8:       addDouble
#>  9:       addDouble
#> 10:      addInteger
#> 11:      addInteger
#> 12:      addInteger
#> 13:      addInteger
#> 14:      addInteger
#> 15:      addInteger
#> 16:      addInteger
#> 17:      addInteger
#> 18:      addInteger
#> 19:    divideByZero
#> 20:    divideByZero
#> 21:    divideByZero
#> 22: generateWarning
#> 23:   generateError
#> 24:  addMultiDouble
#> 25:  addMultiDouble
#> 26:  addMultiDouble
#> 27: addMultiInteger
#> 28: addMultiInteger
#> 29: addMultiInteger
#>       function_name
#>                                                             description
#>  1:                                                       sum 2 doubles
#>  2:                                         sum 1 double and 1 NA_real_
#>  3:                                                      sum 2 NA_real_
#>  4:                                                           sum 2 NAN
#>  5:                                                           sum 2 Inf
#>  6: sum 2 as.integers confused with sum of rounded value as expectation
#>  7:                                       sum of 1 integer and 1 double
#>  8:                                       sum of 1 integer and 1 double
#>  9:                                                            sum 2 NA
#> 10:                                                      sum 2 integers
#> 11:                                      sum 1 integer and 1 NA_integer
#> 12:                                                    sum 2 NA_integer
#> 13:                             sum a converted string with one integer
#> 14:                                          sum 1 integer and 1 double
#> 15:                                             sum 1 integer and 1 Inf
#> 16:                                             sum 1 integer and 1 NAN
#> 17:                                              sum 1 integer and 1 NA
#> 18:                           sum a vector of 2 integers with 1 integer
#> 19:                                                               1 / 0
#> 20:                                                              -1 / 0
#> 21:                                                               0 / 0
#> 22:                                                    generate warning
#> 23:                                                      generate error
#> 24:                                       sum of 1 integer and 1 double
#> 25:                       sum of 1 double, 2 integers and 1 NA_integer_
#> 26:                                                      sum of nothing
#> 27:                                                   sum of 4 integers
#> 28:                       sum of 1 integer, 2 doubles and 1 NA_integer_
#> 29:                                      sum of one integer and nothing
#>                                                             description

8.5 Run a test case

To run a test case, you may use the package function runTestCase.

This runs the test number 4. Result has two parts. A raw part, that holds the intermediate computation results, and a synthesis part that is a data.table provided to ease result interpretation.

You can provide a vector instead of a single test number if you want to run several use test cases in one call.

runTestCase(AdditionTCFIG1(), 12:17, EvaluationMode(defineEvaluationModes()[3]))
#> $raw
#> $raw$addInteger
#> $raw$addInteger$status
#> [1] TRUE
#> 
#> $raw$addInteger$value
#> [1] NA
#> 
#> $raw$addInteger$mode
#> [1] "type_checking_enforcement"
#> 
#> $raw$addInteger$function_return_check
#> [1] TRUE
#> 
#> $raw$addInteger$parameter_check
#> [1] TRUE
#> 
#> $raw$addInteger$parameter_type_checks
#>    parameter_name parameter_value validity             message
#> 1:            x_i              NA     TRUE good type in values
#> 2:            y_i              NA     TRUE good type in values
#> 
#> $raw$addInteger$function_return_type_check
#>    parameter_name parameter_value validity             message
#> 1:            x_i              NA     TRUE good type in values
#> 
#> $raw$addInteger$index
#> [1] 12
#> 
#> $raw$addInteger$value_check
#> [1] TRUE
#> 
#> $raw$addInteger$expected_evaluation
#> [1] "correct"
#> 
#> $raw$addInteger$execution_evaluation
#> [1] "correct"
#> 
#> $raw$addInteger$failure_origin
#> [1] NA
#> 
#> 
#> $raw$addInteger
#> $raw$addInteger$status
#> [1] TRUE
#> 
#> $raw$addInteger$value
#> [1] 89
#> 
#> $raw$addInteger$mode
#> [1] "type_checking_enforcement"
#> 
#> $raw$addInteger$function_return_check
#> [1] TRUE
#> 
#> $raw$addInteger$parameter_check
#> [1] TRUE
#> 
#> $raw$addInteger$parameter_type_checks
#>    parameter_name parameter_value validity             message
#> 1:            x_i              45     TRUE good type in values
#> 2:            y_i              44     TRUE good type in values
#> 
#> $raw$addInteger$function_return_type_check
#>    parameter_name parameter_value validity             message
#> 1:            x_i              89     TRUE good type in values
#> 
#> $raw$addInteger$index
#> [1] 13
#> 
#> $raw$addInteger$value_check
#> [1] TRUE
#> 
#> $raw$addInteger$expected_evaluation
#> [1] "correct"
#> 
#> $raw$addInteger$execution_evaluation
#> [1] "correct"
#> 
#> $raw$addInteger$failure_origin
#> [1] NA
#> 
#> 
#> $raw$addInteger
#> $raw$addInteger$status
#> [1] FALSE
#> 
#> $raw$addInteger$value
#> [1] 78.5
#> 
#> $raw$addInteger$mode
#> [1] "type_checking_enforcement"
#> 
#> $raw$addInteger$function_return_check
#> [1] FALSE
#> 
#> $raw$addInteger$parameter_check
#> [1] FALSE
#> 
#> $raw$addInteger$parameter_type_checks
#>    parameter_name parameter_value validity              message
#> 1:            x_i              34     TRUE  good type in values
#> 2:            y_i            44.5    FALSE wrong type in values
#> 
#> $raw$addInteger$function_return_type_check
#>    parameter_name parameter_value validity              message
#> 1:            x_i            78.5    FALSE wrong type in values
#> 
#> $raw$addInteger$index
#> [1] 14
#> 
#> $raw$addInteger$value_check
#> [1] FALSE
#> 
#> $raw$addInteger$expected_evaluation
#> [1] "failure"
#> 
#> $raw$addInteger$execution_evaluation
#> [1] "failure"
#> 
#> $raw$addInteger$failure_origin
#> [1] "function return type check, parameter check"
#> 
#> 
#> $raw$addInteger
#> $raw$addInteger$status
#> [1] FALSE
#> 
#> $raw$addInteger$value
#> [1] Inf
#> 
#> $raw$addInteger$mode
#> [1] "type_checking_enforcement"
#> 
#> $raw$addInteger$function_return_check
#> [1] FALSE
#> 
#> $raw$addInteger$parameter_check
#> [1] FALSE
#> 
#> $raw$addInteger$parameter_type_checks
#>    parameter_name parameter_value validity              message
#> 1:            x_i              34     TRUE  good type in values
#> 2:            y_i             Inf    FALSE wrong type in values
#> 
#> $raw$addInteger$function_return_type_check
#>    parameter_name parameter_value validity              message
#> 1:            x_i             Inf    FALSE wrong type in values
#> 
#> $raw$addInteger$index
#> [1] 15
#> 
#> $raw$addInteger$value_check
#> [1] TRUE
#> 
#> $raw$addInteger$expected_evaluation
#> [1] "failure"
#> 
#> $raw$addInteger$execution_evaluation
#> [1] "failure"
#> 
#> $raw$addInteger$failure_origin
#> [1] "function return type check, parameter check"
#> 
#> 
#> $raw$addInteger
#> $raw$addInteger$status
#> [1] FALSE
#> 
#> $raw$addInteger$value
#> [1] NaN
#> 
#> $raw$addInteger$mode
#> [1] "type_checking_enforcement"
#> 
#> $raw$addInteger$function_return_check
#> [1] FALSE
#> 
#> $raw$addInteger$parameter_check
#> [1] FALSE
#> 
#> $raw$addInteger$parameter_type_checks
#>    parameter_name parameter_value validity              message
#> 1:            x_i              34     TRUE  good type in values
#> 2:            y_i             NaN    FALSE wrong type in values
#> 
#> $raw$addInteger$function_return_type_check
#>    parameter_name parameter_value validity              message
#> 1:            x_i             NaN    FALSE wrong type in values
#> 
#> $raw$addInteger$index
#> [1] 16
#> 
#> $raw$addInteger$value_check
#> [1] TRUE
#> 
#> $raw$addInteger$expected_evaluation
#> [1] "failure"
#> 
#> $raw$addInteger$execution_evaluation
#> [1] "failure"
#> 
#> $raw$addInteger$failure_origin
#> [1] "function return type check, parameter check"
#> 
#> 
#> $raw$addInteger
#> $raw$addInteger$status
#> [1] FALSE
#> 
#> $raw$addInteger$value
#> [1] NA
#> 
#> $raw$addInteger$mode
#> [1] "type_checking_enforcement"
#> 
#> $raw$addInteger$function_return_check
#> [1] TRUE
#> 
#> $raw$addInteger$parameter_check
#> [1] FALSE
#> 
#> $raw$addInteger$parameter_type_checks
#>    parameter_name parameter_value validity              message
#> 1:            x_i              34     TRUE  good type in values
#> 2:            y_i              NA    FALSE wrong type in values
#> 
#> $raw$addInteger$function_return_type_check
#>    parameter_name parameter_value validity             message
#> 1:            x_i              NA     TRUE good type in values
#> 
#> $raw$addInteger$index
#> [1] 17
#> 
#> $raw$addInteger$value_check
#> [1] TRUE
#> 
#> $raw$addInteger$expected_evaluation
#> [1] "failure"
#> 
#> $raw$addInteger$execution_evaluation
#> [1] "failure"
#> 
#> $raw$addInteger$failure_origin
#> [1] "parameter check"
#> 
#> 
#> 
#> $synthesis
#>    status                      mode index value_check
#> 1:   TRUE type_checking_enforcement    12        TRUE
#> 2:   TRUE type_checking_enforcement    13        TRUE
#> 3:  FALSE type_checking_enforcement    14       FALSE
#> 4:  FALSE type_checking_enforcement    15        TRUE
#> 5:  FALSE type_checking_enforcement    16        TRUE
#> 6:  FALSE type_checking_enforcement    17        TRUE
#>    function_return_check parameter_check expected_evaluation
#> 1:                  TRUE            TRUE             correct
#> 2:                  TRUE            TRUE             correct
#> 3:                 FALSE           FALSE             failure
#> 4:                 FALSE           FALSE             failure
#> 5:                 FALSE           FALSE             failure
#> 6:                  TRUE           FALSE             failure
#>    execution_evaluation                              failure_origin
#> 1:              correct                                        <NA>
#> 2:              correct                                        <NA>
#> 3:              failure function return type check, parameter check
#> 4:              failure function return type check, parameter check
#> 5:              failure function return type check, parameter check
#> 6:              failure                             parameter check

Looking at synthesis, you will discover that test 17 fails under chosen evaluation mode, and therefore should require a fix. Here looking at raw results for test number 17, brings solution, that is about input parameter compliance. Provided values are double, whereas integers were expected.