Skip to content

grade_this() allows instructors to write custom logic to evaluate, grade and give feedback to students. To use grade_this(), call it directly in your *-check chunk:

```{r example-check}
grade_this({
  # custom checking code appears here
  if (identical(.result, .solution)) {
    pass("Great work!")
  }
  fail("Try again!")
})
```

grade_this() makes available a number of objects based on the exercise and the student's submission that can be used to evaluate the student's submitted code. See ?"grade_this-objects" for more information about these objects.

As the instructor, you are free to use any logic to determine a student's grade as long as a graded() object is signaled. The check code can also contain testthat expectation code. Failed testthat expectations will be turned into fail()ed grades with the corresponding message.

A final grade is signaled from grade_this() using the graded() helper functions, which include pass(), fail(), among others. grade_this() uses condition handling to short-circuit further evaluation when a grade is reached. This means that you may also signal a failing grade using any of the expect_*() functions from testthat, other functions designed to work with testthat, such as checkmate, or standard R errors via stop(). Learn more about this behavior in graded() in the section Return a grade immediately.

Usage

grade_this(
  expr,
  ...,
  maybe_code_feedback = getOption("gradethis.maybe_code_feedback", TRUE)
)

Arguments

expr

The grade-checking expression to be evaluated. This expression must either signal a grade via pass() or fail() functions or their sibling functions.

By default, errors in this expression are converted to "internal problem" grades that mask the error for the user. If your grading logic relies on unit-test-styled functions, such as those from testthat, you can use fail_if_error() to convert errors into fail() grades.

...

Ignored

maybe_code_feedback

Should maybe_code_feedback() provide code feedback when used in a graded() message? The default value can be set with gradethis_setup().

Typically, maybe_code_feedback() is called in the default fail() message (the default can be customized the fail argument of gradethis_setup()). If the maybe_code_feedback argument is FALSE, maybe_code_feedback() returns an empty string.

Value

Returns a function whose first parameter will be an environment containing objects specific to the exercise and submission (see Available variables). For local testing, you can create a version of the expected environment for a mock exercise submission with mock_this_exercise(). Calling the returned function on the exercise-checking environment will evaluate the grade-checking expr and return a final grade via graded().

Examples

# For an interactive example run: gradethis_demo()

# Suppose we have an exercise that prompts students to calculate the
# average height of Loblolly pine trees using the `Loblolly` data set.
# We might write an exercise `-check` chunk like the one below.
#
# Since grade_this() returns a function, we'll save the result of this
# "chunk" as `grader()`, which can be called on an exercise submission
# to evaluate the student's code, which we'll simulate with
# `mock_this_exercise()`.

grader <-
  # ```{r example-check}
  grade_this({
    if (length(.result) != 1) {
      fail("I expected a single value instead of {length(.result)} values.")
    }

    if (is.na(.result)) {
      fail("I expected a number, but your code returned a missing value.")
    }

    avg_height <- mean(Loblolly$height)
    if (identical(.result, avg_height)) {
      pass("Great work! The average height is {round(avg_height, 2)}.")
    }

    # Always end grade_this() with a default grade.
    # By default fail() will also give code feedback,
    # if a solution is available.
    fail()
  })
# ```

# Simulate an incorrect answer: too many values...
grader(mock_this_exercise(.user_code = Loblolly$height[1:2]))
#> <gradethis_graded: [Incorrect]
#>   I expected a single value instead of 2 values.
#> >

# This student submission returns a missing value...
grader(mock_this_exercise(mean(Loblolly$Seed)))
#> Warning: argument is not numeric or logical: returning NA
#> <gradethis_graded: [Incorrect]
#>   I expected a number, but your code returned a missing value.
#> >
# This student submission isn't caught by any specific tests,
# the final grade is determined by the default (last) value in grade_this()
grader(mock_this_exercise(mean(Loblolly$age)))
#> <gradethis_graded: [Incorrect]
#>   Incorrect. Try it again; next time's the charm!
#> >

# If you have a *-solution chunk,
# fail() without arguments gives code feedback...
grader(
  mock_this_exercise(
    .user_code = mean(Loblolly$age),
    .solution_code = mean(Loblolly$height)
  )
)
#> <gradethis_graded: [Incorrect]
#>   Incorrect. In `Loblolly$age`, I expected `height` where you
#>   wrote `age`. Try it again. Perseverence is the key to success.
#> >

# Finally, the "student" gets the correct answer!
grader(mock_this_exercise(mean(Loblolly$height)))
#> <gradethis_graded: [Correct]
#>   Great work! The average height is 32.36.
#> >