-1

I'm using testthat package for unit testing in R. I have a function CalcByResultSubModel which has one more function CalculateX which is called inside the main function. This is the main function,


CalcByResultSubModel = function(doll_data, fn_master, modelPath) {
  # load sub model result
  load(modelPath)
  
  # calculation
  for(abc in c("ABC", fn_master$fn_a)) {
    
    # columns
    col_name = paste0("x", abc)    
    iterModel = resultSubmodel[[abc]]
    
    # calculate yhat X
    doll_data[, col_name] = iterModel %>% 
      purrr::map(., function(imodel) {
        CalculateX(data, imodel)
      }) %>% 
      as.data.frame(.) %>%
      apply(., 1, mean)
    
    message(paste(col_name, "calculated"))      
  }

This is the function CalculateX

CalculateX = function(data, model) {
  iterData = data %>% 
    dplyr::select(model$feature_names) %>% 
    as.matrix(.)
  set.seed(131)
  result = predict(model, iterData, missing = NA)
  result = matrix(result, 2)[2, ]
  
  return(result)
}

Inorder to perform unit testing we have to mock the function CalculateX. But the complexity here is that, the function is called inside for loop in the main function. I'm quite new to this scenario in my unit testing. Can anyone help me with the mocking of the function in a for loop? This is the code for mocking and I tried this.

local_mock(CalculateX = function(data, model){
                  for (abc in c("ABC", fn_master$fn_a)
                  case_when(
                    abc == "feature1" ~ .ReadCsvWrapper("feature1.csv"),
                    abc == "feature2" ~ .ReadCsvWrapper( "feature2.csv"),
                    abc == "feature3" ~ .ReadCsvWrapper("feature3.csv"))
            })
But the above approach doesn't seem to work for me. Can anyone help me with this?
Luiy_coder
  • 321
  • 2
  • 11
  • You don't say how you were doing the mocking, or what went wrong. Could you add that to your question? Otherwise it's not obvious why the for loop makes any difference at all. – user2554330 Jun 14 '21 at 13:59
  • Yes I will add the mocking part which I tried. – Luiy_coder Jun 14 '21 at 14:19

1 Answers1

0

There are a couple of problems in your code. First, CalcByResultSubModel calls CalculateX in the loop

for(abc in c("ABC", fn_master$fn_a)) {
    
    # columns
    col_name = paste0("x", abc)    
    iterModel = resultSubmodel[[abc]]
    
    # calculate yhat X
    doll_data[, col_name] = iterModel %>% 
      purrr::map(., function(imodel) {
        CalculateX(data, imodel)
      }) %>% 
      as.data.frame(.) %>%
      apply(., 1, mean)
    
    message(paste(col_name, "calculated"))      
  }

so you don't need to put that for(abc in c("ABC", fn_master$fn_a)) into the mocked function. Just set it up to return results similar to what one call to the real function would do.

The second problem is that in the real CalculateX, you have set.seed(131). This is almost certainly a bad idea. It resets the random number generator to a fixed setting every time CalculateX is called, which makes it completely non-random, and also makes calls to random number functions afterwards repeat their outputs.

It's often a good idea to set the seed once at the top of your testing script so that tests are predictable, but resetting it as often as you did is not.

user2554330
  • 37,248
  • 4
  • 43
  • 90