R Programming Quiz: Testthat
10 questions · exam conditions
0:00
TestthatQuestion 1 of 10

The object scores is created with scores <- c(math = 8, code = 9). The expectation expect_equal(scores, c(8, 9)) fails because the expected vector lacks the same names.

Which replacement expectation should pass without modifying scores?

expect_equal(scores, c(code = 9, math = 8))
expect_equal(scores, list(math = 8, code = 9))
expect_equal(scores, c(math = 8, code = 9))
expect_equal(unname(scores), c(math = 8, code = 9))
← Back to quizzes

R Programming Quiz

R Programming Quiz: Testthat

Practice Testthat in R Programming with focused quiz questions that help you check what you know, review explanations, and build confidence with test-style prompts.

What this quiz covers

This quiz focuses on Testthat, giving you a quick way to practice the rules, question types, and explanations that matter most for R Programming.

How to use this quiz

Try each quiz question before looking at the correct answer. Use the explanations to review missed ideas, then come back to similar questions until the pattern feels familiar.

All questions

Question 1

The object scores is created with scores <- c(math = 8, code = 9). The expectation expect_equal(scores, c(8, 9)) fails because the expected vector lacks the same names.

Which replacement expectation should pass without modifying scores?

  1. expect_equal(scores, c(code = 9, math = 8))
  2. expect_equal(scores, list(math = 8, code = 9))
  3. expect_equal(scores, c(math = 8, code = 9)) (correct answer)
  4. expect_equal(unname(scores), c(math = 8, code = 9))
Explanation: When testing named vectors in R, you need to understand that expect_equal() checks both values and attributes — and names are stored as an attribute. This means two vectors with identical values but different names are considered unequal. Since scores is c(math = 8, code = 9), any passing expectation must match both the numeric values and the names exactly. Option C, expect_equal(scores, c(math = 8, code = 9)), does exactly this — the expected vector has the same values in the same order with the same names, so the comparison succeeds. This is the correct answer. Option A fails because while the values are correct, the names are in the wrong order (code = 9, math = 8). Since expect_equal() checks element-by-element, the name "code" at position 1 doesn't match "math" at position 1, causing failure. Option B fails for a more fundamental reason: list(math = 8, code = 9) creates a list, not a vector — these are entirely different data structures in R, and expect_equal() will flag the class mismatch immediately. Option D is a clever-looking trap: unname(scores) strips the names from scores, producing c(8, 9), but then the expected value is c(math = 8, code = 9) which has names — so now the situation is reversed and still fails. A useful rule of thumb: when writing test expectations for named vectors, always construct your expected value with the exact names and order as the object being tested. If you want to ignore names intentionally, strip them from both sides with unname().

Question 2

A function and test are defined as follows: ratio <- function(a, b) { if (b == 0) stop("zero denominator"); a / b }; test_that("zero produces infinity", { expect_equal(ratio(6, 0), Inf) }).

How is this test evaluated?

  1. It passes because division by zero normally produces Inf for positive numeric values.
  2. It fails as an error because ratio(6, 0) stops before comparison occurs. (correct answer)
  3. It passes because expect_equal() converts the error message into an infinite value.
  4. It fails as an inequality because the returned value is NA rather than Inf.
Explanation: When testing functions that include error-handling logic, you need to think carefully about when that logic fires and how it interacts with your test expectations. Here, ratio(6, 0) never reaches the division step — it hits stop("zero denominator") first, which throws a condition error that propagates immediately out of the function call. Because expect_equal() is not designed to catch errors, it never gets a value to compare against Inf. Instead, the error propagates through the test, causing it to fail with an error condition rather than a simple pass/fail assertion result. That makes B correct: the test fails as an error, not as an inequality. A is tempting because it's true that R's base arithmetic returns Inf for 1/0 — but that only happens when there's no stop() guard in the way. This function explicitly prevents that result. C misunderstands what expect_equal() does; it compares two values, it doesn't rescue or transform error messages. If an error is thrown inside the test block, expect_equal() has nothing to work with. D is wrong because NA would only appear in certain undefined arithmetic contexts (like 0/0 producing NaN, or missing data), not when stop() is called — which halts execution entirely. The key study takeaway: whenever you see a function with stop(), warning(), or tryCatch(), ask yourself whether the test is designed to expect that error using expect_error(). If it isn't, any thrown error will cause the test to fail abnormally, regardless of what value you wrote in the assertion.

Question 3

Consider keep_positive <- function(x) x[x > 0] and the test test_that("keeps positive entries", { result <- keep_positive(c(-2, 0, 3, 1)); expect_equal(result, c(3, 1)); expect_equal(length(result), 3) }).

Which statement correctly describes the test results?

  1. Both expectations pass because zero is treated as a positive entry in R comparisons.
  2. Both expectations fail because logical indexing reorders the positive entries.
  3. The value expectation fails, but the length expectation passes.
  4. The value expectation passes, but the length expectation fails. (correct answer)
Explanation: When you see a question combining logical indexing with testthat expectations, trace through the actual R output step by step before evaluating each expectation separately. Here, keep_positive(c(-2, 0, 3, 1)) applies the condition x > 0. In R, zero is not greater than zero, so the logical mask is c(FALSE, FALSE, TRUE, TRUE). Indexing with this mask returns c(3, 1) — only the two values that are strictly positive. Now check each expectation: expect_equal(result, c(3, 1)) compares c(3, 1) to c(3, 1) — this passes. Then expect_equal(length(result), 3) checks whether length(c(3, 1)) equals 3, but length(c(3, 1)) is 2, not 3. This fails. That makes D the correct answer — the value expectation passes, but the length expectation fails. A is wrong because it mischaracterizes how R handles zero: 0 > 0 evaluates to FALSE, so zero is never retained by this filter. B is wrong on two counts — logical indexing preserves order, and not both expectations fail. C has the outcome exactly backwards: it's the length check that fails, not the value check. A useful strategy here is to mentally "run" the function on the given input before judging the test assertions — write out the resulting vector, then evaluate each expect_* call independently. A common trap on testing questions is assuming all assertions in one test_that block share the same pass/fail outcome, but each expectation is evaluated on its own.

Question 4

Consider the following test: shift <- function(x) x - x[1]; test_that("shift uses the first value as the baseline", { y <- shift(c(3, 5, 8)); expect_equal(y, c(0, 2, 5)); expect_equal(sum(y), 8) }).

What happens when this test is run?

  1. Both expectations pass because shift() preserves the differences among the input values.
  2. The first expectation passes, but the expectation about sum(y) fails. (correct answer)
  3. The first expectation fails, but the expectation about sum(y) passes.
  4. Both expectations fail because subtracting a vector element changes the input length.
Explanation: When working with testthat in R, remember that each expect_* call is evaluated independently — a passing expectation doesn't stop the others from being checked, but a failing one doesn't automatically invalidate earlier ones either. Let's trace through the logic. Given shift <- function(x) x - x[1], calling shift(c(3, 5, 8)) subtracts x[1] = 3 from every element: c(3-3, 5-3, 8-3) = c(0, 2, 5). So y = c(0, 2, 5), and the first expectation expect_equal(y, c(0, 2, 5)) passes. Now check sum(y): 0 + 2 + 5 = 7, not 8. The second expectation expect_equal(sum(y), 8) therefore fails. That makes B the correct answer. A is wrong because it concludes both pass — but the sum of the shifted vector is 7, not 8, so the second expectation fails. The function does preserve differences, but that doesn't mean the sum stays the same. C is wrong because it claims the first expectation fails. As shown above, shift(c(3, 5, 8)) returns exactly c(0, 2, 5), so the first expectation passes cleanly. D is wrong on a factual basis — subtracting a scalar (or single element) from a vector in R does not change the vector's length. R applies element-wise recycling, so the output length is identical to the input length. Study tip: When tracing testthat code, always compute intermediate values explicitly — don't assume a function works correctly just because its description sounds reasonable. Verify each expectation by hand.

Question 5

Each choice below is used as the only expectation inside a separate test_that() block.

Which expectation passes?

  1. expect_equal(c(1, NA_real_), c(1, NA_real_)) (correct answer)
  2. expect_equal(c(1, NA_real_), c(1, NaN))
  3. expect_equal(c(1, NA_real_), c(1, 0))
  4. expect_equal(c(1, NA_real_), c(NA_real_, 1))
Explanation: When working with testthat expectations in R, a key skill is understanding exactly how expect_equal() compares values — particularly around special values like NA, NaN, and ordering. expect_equal() performs an element-wise comparison and requires both objects to be structurally and value-identical. This means position matters, types matter, and special values are treated distinctly. Choice A passes because both vectors are c(1, NA_real_) — identical in length, order, and type. The NA_real_ values align perfectly, so the comparison succeeds. Choice B fails because NA_real_ and NaN are different special values in R. NA_real_ represents a missing value, while NaN ("Not a Number") represents an undefined mathematical result like 0/0. Although both are "non-finite," expect_equal() does not treat them as interchangeable — is.na(NaN) returns TRUE, but identical(NA_real_, NaN) returns FALSE, and expect_equal() detects this mismatch. Choice C fails for the most obvious reason: NA_real_ and 0 are simply different values — one is missing, the other is a real number. No special behavior is needed to explain this failure. Choice D is a subtle trap. The two vectors contain the same elements but in reversed order: c(1, NA_real_) vs. c(NA_real_, 1). expect_equal() is position-sensitive, so element 1 (1 vs. NA_real_) and element 2 (NA_real_ vs. 1) both fail to match. Study tip: Remember that NA, NA_real_, and NaN are distinct in R — never assume they're interchangeable. When in doubt, test with identical() to predict what expect_equal() will do.

Question 6

A test contains these expectations: expect_equal(0.1 + 0.2, 0.3) and expect_equal(0.1 + 0.2, 0.3, tolerance = 0).

Which outcome is expected under standard testthat numeric comparison behavior?

  1. Both expectations pass because R stores both calculations as the same numeric value.
  2. The default-tolerance expectation passes, while the zero-tolerance expectation fails. (correct answer)
  3. The default-tolerance expectation fails, while the zero-tolerance expectation passes.
  4. Both expectations fail because expect_equal() always requires bit-for-bit numeric identity.
Explanation: Floating-point arithmetic is a frequent source of confusion in testing, and this question tests whether you understand how testthat handles the gap between mathematical ideals and computer reality. When R computes 0.1 + 0.2, the result is not exactly 0.3 at the binary level — it's something like 0.30000000000000004. This is a fundamental limitation of IEEE 754 floating-point representation, not an R quirk. Because of this, a strict bit-for-bit comparison between 0.1 + 0.2 and 0.3 will fail. testthat's expect_equal() anticipates this problem by using a small default tolerance (approximately 1.5e-8), which allows values that are "close enough" to be considered equal. Under that default behavior, expect_equal(0.1 + 0.2, 0.3) passes. However, setting tolerance = 0 demands exact equality, which the floating-point result cannot satisfy — so that second expectation fails. This makes B the correct answer. A is wrong because R does not store both as the same value; the floating-point discrepancy is real and measurable. C has the logic exactly backwards — it's the default-tolerance test that passes, not the zero-tolerance one. D is wrong because expect_equal() explicitly avoids requiring bit-for-bit identity by design; that's the whole point of its tolerance parameter. A useful rule of thumb: never assume floating-point arithmetic produces mathematically exact results in code. When writing or reading tests involving decimals, always ask yourself whether a tolerance is being applied — that distinction determines whether the test passes or fails.

Question 7

A test includes expect_equal(c(2, 4, 6), c(2, 4)).

Why does this expectation fail even though the first two elements match?

  1. expect_equal() requires matching vector lengths and does not recycle the shorter expected vector. (correct answer)
  2. expect_equal() compares only the sums of both vectors, and their totals differ.
  3. expect_equal() recycles the shorter vector during comparison, making the third element 4 instead of 6.
  4. expect_equal() sorts both vectors before comparing, so their final elements disagree.
Explanation: When working with testthat in R, it helps to think of expect_equal() as a strict equality check — it compares two objects as complete units, not element by element in isolation. expect_equal(c(2, 4, 6), c(2, 4)) fails because the two vectors have different lengths: one has three elements, the other has two. The function checks that both objects are identical in structure and content, which means length matters. Even though the first two elements align perfectly, the mismatch in length alone is enough to trigger a failure. This is why A is correctexpect_equal() requires matching lengths and does not silently pad or recycle the shorter vector to make them comparable. B describes a completely fictional behavior. expect_equal() never sums the vectors and compares totals; that would make it nearly useless for unit testing, since many different vectors share the same sum. C describes R's recycling behavior, which does appear in base R operations like arithmetic or logical comparisons. However, expect_equal() does not apply recycling — it is explicitly designed to catch structural differences like mismatched lengths, not hide them. D is also invented. expect_equal() preserves element order and does not sort either vector before comparing. Order-sensitive comparisons are a feature, not something the function works around. A useful rule of thumb: expect_equal() behaves like asking "are these two objects the same in every way?" — length, values, and order all count. Whenever a test involves vectors of different lengths, expect it to fail regardless of any partial matches.

Question 8

Assume identity_vec <- function(x) x. The following test is run: test_that("returns values in reverse order", { expect_equal(identity_vec(1:3), 1:3) }).

What is the result, given that the test description does not match the behavior being asserted?

  1. The test passes because only the expectation determines whether the test succeeds. (correct answer)
  2. The test fails because the description requires the result to be reversed.
  3. The test is skipped because the description conflicts with the expected vector.
  4. The test errors because test_that() validates descriptions against function output.
Explanation: When working with testthat in R, it's crucial to understand what actually controls whether a test passes or fails. The test description string passed to test_that() is purely documentary — it's a human-readable label meant to communicate intent, nothing more. R never parses or evaluates that string as a behavioral constraint. In this example, identity_vec(1:3) simply returns 1:3, and expect_equal(identity_vec(1:3), 1:3) checks whether the output equals 1:3 — which it does. The test passes, making A correct. The misleading description ("returns values in reverse order") is irrelevant to the outcome because testthat has no mechanism to cross-check descriptions against expectations. B is wrong because the description string carries zero logical weight in R's testing framework. Failing a test requires a failed expectation (like expect_equal), not a mismatched label. C is wrong because testthat has no "skip-on-conflict" behavior — tests are skipped only when you explicitly call skip() or related functions. D is wrong because test_that() does not validate or interpret descriptions at all; it simply uses the string for output labeling when a test fails, making it easier to identify which test broke. A useful mental model: think of the description as a sticky note on the outside of a box. R reads what's inside the box (the expectations) to decide pass or fail — it never checks whether the sticky note matches the contents. On exam questions like this, always trace the actual expectation call to determine the result, and ignore any temptation to treat the description string as executable logic.

Question 9

The intended conversion function is fahrenheit <- function(celsius) celsius * 9 / 5 + 32. Suppose a defect changes the multiplier from 9 / 5 to 5 / 9.

Which test is most directly capable of detecting this defect?

  1. test_that("zero converts", { expect_equal(fahrenheit(0), 32) })
  2. test_that("one value returns", { expect_equal(length(fahrenheit(10)), 1) })
  3. test_that("ten converts", { expect_equal(fahrenheit(10), 50) }) (correct answer)
  4. test_that("bug formula matches", { expect_equal(fahrenheit(10), 10 * 5 / 9 + 32) })
Explanation: When writing unit tests, your goal is to catch specific defects — not just confirm that code runs or returns something plausible. The key question here is: which test would produce a wrong result under the buggy formula (celsius * 5/9 + 32) but a correct result under the intended formula (celsius * 9/5 + 32)? C is the right choice because fahrenheit(10) should equal 50 under the correct formula: 10×95+32=18+32=50.10 \times \frac{9}{5} + 32 = 18 + 32 = 50. Under the defective formula, the result is 10×59+3237.56,10 \times \frac{5}{9} + 32 \approx 37.56, which clearly fails expect_equal(..., 50). This test directly exposes the multiplier defect. A fails to detect anything because 0°C is a degenerate input — both formulas give the same result: 0×95+32=0×59+32=32.0 \times \frac{9}{5} + 32 = 0 \times \frac{5}{9} + 32 = 32. A zero input cancels out the multiplier entirely, so the bug is invisible. B tests only that the output has length 1, which says nothing about correctness. The buggy function still returns a single number — just the wrong one. D is a trap: it hardcodes the buggy formula as the expected value. A test that expects the wrong answer will pass precisely when the code is broken — the opposite of what a good test should do. Study tip: Always choose non-zero, non-trivial inputs for arithmetic tests, and never write expected values derived from the code under test itself — that just validates the bug, not the behavior.

Question 10

A test contains observed <- c(0.5000004, 0.700002) followed by expect_equal(observed, c(0.5, 0.7), tolerance = 1e-6).

What should happen to this expectation?

  1. It passes because both element differences fall within the supplied tolerance.
  2. It fails because supplying any explicit tolerance causes expect_equal() to reject nonzero differences.
  3. It passes because only the first element needs to satisfy the tolerance check.
  4. It fails because the second element differs from its expected value by more than the supplied tolerance. (correct answer)
Explanation: When working with expect_equal() in the testthat package, the tolerance argument sets the maximum allowable absolute difference between each corresponding pair of elements — and every element must satisfy that threshold independently. Here, observed <- c(0.5000004, 0.700002) is compared against c(0.5, 0.7) with tolerance = 1e-6. Let's check both elements:
  • First element: 0.50000040.5=4×107=0.0000004|0.5000004 - 0.5| = 4 \times 10^{-7} = 0.0000004, which is less than 1×1061 \times 10^{-6}
  • Second element: 0.7000020.7=2×106=0.000002|0.700002 - 0.7| = 2 \times 10^{-6} = 0.000002, which exceeds 1×1061 \times 10^{-6}
Because the second element's difference is twice the tolerance, the expectation fails — making D the correct answer. A is wrong because it assumes both differences fall within tolerance. Only the first one does; the second does not. B reflects a fundamental misunderstanding — supplying an explicit tolerance doesn't disqualify nonzero differences outright; it's precisely how you permit small differences. C describes a logic that doesn't exist in expect_equal(): all elements are checked, not just the first. A useful strategy: when you see tolerance-based comparisons in testing questions, always compute the absolute difference for each element separately and compare it against the threshold. Don't assume that passing one element means the test passes overall — expect_equal() applies the tolerance check element-wise across the entire vector.