datacamp / testwhat

Write Submission Correctness Tests for R exercises
https://datacamp.github.io/testwhat
GNU Affero General Public License v3.0
33 stars 24 forks source link

Add check_aes() for gplot2 aesthetics #190

Open richierocks opened 5 years ago

richierocks commented 5 years ago

I'm just starting to go through the ggplot2 courses updating SCTs to not use test_ggplot().

For a simple plot like this:

p <- ggplot(mtcars, aes(x = cyl, y = mpg)) +
  geom_point()

I'm writing SCTs like this:

ex() %>% {
  check_function(., 'ggplot') %>% {
      check_arg(., 'data') %>% check_equal()
      check_arg(., 'mapping') %>% check_function('aes') %>% {
        check_arg(., 'x') %>% check_equal(eval = FALSE)
        check_arg(., 'y') %>% check_equal(eval = FALSE)
      }
  }
  check_function(., 'geom_point')
  check_error(.)
}

So far, the most unsatisfying part is checking the aesthetics, since you always have to do eval = FALSE.

ggplot2 v3 is built on top of rlang, so I can now do

ex() %>% 
  check_expr("rlang::eval_tidy(p$mapping$x, p)$data)") %>% 
  check_result() %>% 
  check_equal()

This is great because I can check the object (and rlang is always installed in the image because ggplot2 imports it).

There are 2 small problems:

  1. It's not intuitive, and
  2. It requires a custom message every time because "Running rlang::eval_tidy(p$mapping$x, p$data) didn't give the correct result." will scare all but the bravest students.

I think these problems can be nicely solved by having a check_aes() that wraps this functionality.

Something like

check_aes <- function(state, plot, aesthetic) {
  cmd <- sprintf("rlang::eval_tidy(%s$mapping[[%s]], %s$data)", plot, aesthetic, plot)
  aes_error_msg <- sprintf("Evaluating the %s aesthetic for plot %s threw an error.", aesthetic, plot)
  aes_incorrect_msg <- sprintf("The %s aesthetic for plot %s has the wrong value.", aesthetic, plot)
  ex() %>% 
    check_expr(cmd) %>% 
    check_result(error_msg = aes_error_msg) %>% 
    check_equal(incorrect_msg = aes_incorrect_msg)
}

One bigger problem is that it requires that the student assign the plot to a variable. I thought I could use last_plot() to retrieve the last plot that the students drew, but evaluation of that plot fails.

ex() %>% 
  check_expr("rlang::eval_tidy(last_plot()$mapping$x, last_plot()$data)") %>% 
  check_result() %>% 
  check_equal()

last_plot() effectively does asNamespace("ggplot2")$.store$get(), and I've no idea how testwhat tries to evaluate that.

filipsch commented 5 years ago

@richierocks I currently do not have time for this, but feel free to add this utility function in testwhat_ext.. If you make a PR, I'll gladly review it.