faken
faken

Reputation: 6852

How to test a function with a combination of parameters without resorting to a for loop

In R, is it possible to test a function with a combination of parameters without resorting to a for loop? For example, I'm currently doing something similar to this:

test_that("Function myfunction() works properly", {

  a1 <- c(1, 2, 3, 10, 20, 100)
  a2 <- c(-500, 0, 500)
  a3 <- c("hello", "world")

  for (a1i in a1) {
    for (a2i in a2) {
      for (a3i in a3) {

        result <- myfunction(a1i, a2i, a3i)
        expect_equal(result, something_expected)
        expect_equal(dim(result), something_else)
        # ...and other checks...

      }
    }
  }
})

However, this is not practical with too many nested fors, and also raises cyclomatic complexity errors with lintr.

In Python we can easily do this with pytest (using test parameters or text fixtures), and this is also easy to achieve in Julia.

I found the patrick package, but it doesn't seem to do parameter combination in this way, only to define parameter sets. I guess one could create these parameter sets for patrick using for loops, but that seems to be missing the point.

Upvotes: 1

Views: 412

Answers (2)

AndrewGB
AndrewGB

Reputation: 16856

Here is a possible option using base R (also if you want to avoid the for loop), but following at @Paulsmith with expand.grid.

invisible(apply(df, 1, function(x){
    cat(paste0(paste(x[1],x[2],x[3],sep=' '),"\n"))
  }))

Output

  1 -500 hello
  2 -500 hello
  3 -500 hello
 10 -500 hello
 20 -500 hello
100 -500 hello
  1    0 hello
  2    0 hello
  3    0 hello
 10    0 hello
 20    0 hello
100    0 hello
  1  500 hello
  2  500 hello
  3  500 hello
 10  500 hello
 20  500 hello
100  500 hello
  1 -500 world
  2 -500 world
  3 -500 world
 10 -500 world
 20 -500 world
100 -500 world
  1    0 world
  2    0 world
  3    0 world
 10    0 world
 20    0 world
100    0 world
  1  500 world
  2  500 world
  3  500 world
 10  500 world
 20  500 world
100  500 world

Data

a1 <- c(1, 2, 3, 10, 20, 100)
a2 <- c(-500, 0, 500)
a3 <- c("hello", "world")

df <- expand.grid(a1,a2,a3, stringsAsFactors = FALSE) 

Upvotes: 0

PaulS
PaulS

Reputation: 25333

If you want to avoid the for loop at all, then you can use the map functions of package purrr to iterate over the dataframe, which resulted from expand.grid:

a1 <- c(1, 2, 3, 10, 20, 100)
a2 <- c(-500, 0, 500)
a3 <- c("hello", "world")

df <- expand.grid(a1,a2,a3, stringsAsFactors = FALSE) 

# purrr::pwalk(df, ~ cat(..1, ..2, ..3, "\n")) <-- avoiding for loop

for (i in 1:nrow(df))
  cat(df[i,1], df[i, 2], df[i, 3], "\n")
#> 1 -500 hello 
#> 2 -500 hello 
#> 3 -500 hello 
#> 10 -500 hello 
#> 20 -500 hello 
#> 100 -500 hello 
#> 1 0 hello 
#> 2 0 hello 
#> 3 0 hello 
#> 10 0 hello 
#> 20 0 hello 
#> 100 0 hello 
#> 1 500 hello 
#> 2 500 hello 
#> 3 500 hello 
#> 10 500 hello 
#> 20 500 hello 
#> 100 500 hello 
#> 1 -500 world 
#> 2 -500 world 
#> 3 -500 world 
#> 10 -500 world 
#> 20 -500 world 
#> 100 -500 world 
#> 1 0 world 
#> 2 0 world 
#> 3 0 world 
#> 10 0 world 
#> 20 0 world 
#> 100 0 world 
#> 1 500 world 
#> 2 500 world 
#> 3 500 world 
#> 10 500 world 
#> 20 500 world 
#> 100 500 world

Upvotes: 3

Related Questions