testthat - R Programming

  1. What should happen to this expectation?
    Answer: It fails because the second element differs from its expected value by more than the supplied tolerance. 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.5000004−0.5∣=4×10−7=0.0000004|0.5000004 - 0.5| = 4 \times 10^{-7} = 0.0000004, which is less than 1×10−61 \times 10^{-6} ✓ - Second element: ∣0.700002−0.7∣=2×10−6=0.000002|0.700002 - 0.7| = 2 \times 10^{-6} = 0.000002, which exceeds 1×10−61 \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.
  2. Which outcome is expected under standard testthat numeric comparison behavior?
    Answer: The default-tolerance expectation passes, while the zero-tolerance expectation fails. 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.
  3. Why does this expectation fail even though the first two elements match?
    Answer: expect_equal() requires matching vector lengths and does not recycle the shorter expected vector. 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 correct — expect_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.
  4. Which test is most directly capable of detecting this defect?
    Answer: test_that("ten converts", { expect_equal(fahrenheit(10), 50) }) 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+32≈37.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.
  5. Which statement correctly describes the test results?
    Answer: The value expectation passes, but the length expectation fails. 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.
  6. How is this test evaluated?
    Answer: It fails as an error because ratio(6, 0) stops before comparison occurs. 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.
  7. Which replacement expectation should pass without modifying scores?
    Answer: expect_equal(scores, c(math = 8, code = 9)) 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().
  8. What happens when this test is run?
    Answer: The first expectation passes, but the expectation about sum(y) fails. 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.
  9. What is the result, given that the test description does not match the behavior being asserted?
    Answer: The test passes because only the expectation determines whether the test succeeds. 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.
  10. Which expectation passes?
    Answer: expect_equal(c(1, NA_real_), c(1, NA_real_)) 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.

Start this quiz