r-testing

R testing patterns: testthat 3e with expect_* assertions, snapshot testing, mocking with mockery and httptest2, covr code coverage, lintr static analysis, property-based testing with hedgehog, testing Shiny apps with shinytest2. Use when writing or reviewing R tests.

8 stars

Best use case

r-testing is best used when you need a repeatable AI agent workflow instead of a one-off prompt.

R testing patterns: testthat 3e with expect_* assertions, snapshot testing, mocking with mockery and httptest2, covr code coverage, lintr static analysis, property-based testing with hedgehog, testing Shiny apps with shinytest2. Use when writing or reviewing R tests.

Teams using r-testing should expect a more consistent output, faster repeated execution, less prompt rewriting.

When to use this skill

  • You want a reusable workflow that can be run more than once with consistent structure.

When not to use this skill

  • You only need a quick one-off answer and do not need a reusable workflow.
  • You cannot install or maintain the underlying files, dependencies, or repository context.

Installation

Claude Code / Cursor / Codex

$curl -o ~/.claude/skills/r-testing/SKILL.md --create-dirs "https://raw.githubusercontent.com/marvinrichter/clarc/main/skills/r-testing/SKILL.md"

Manual Installation

  1. Download SKILL.md from GitHub
  2. Place it in .claude/skills/r-testing/SKILL.md inside your project
  3. Restart your AI agent — it will auto-discover the skill

How r-testing Compares

Feature / Agentr-testingStandard Approach
Platform SupportNot specifiedLimited / Varies
Context Awareness High Baseline
Installation ComplexityUnknownN/A

Frequently Asked Questions

What does this skill do?

R testing patterns: testthat 3e with expect_* assertions, snapshot testing, mocking with mockery and httptest2, covr code coverage, lintr static analysis, property-based testing with hedgehog, testing Shiny apps with shinytest2. Use when writing or reviewing R tests.

Where can I find the source code?

You can find the source code on GitHub using the link provided at the top of the page.

Related Guides

SKILL.md Source

# R Testing

## When to Activate

- Writing tests with testthat in R packages
- Mocking HTTP calls or functions in tests
- Setting up coverage reporting with covr
- Testing Shiny applications with shinytest2
- Choosing between `expect_equal` and `expect_identical` for numeric vs. exact comparison
- Using snapshot testing to lock in the printed output of S3 objects or complex data structures
- Enforcing a minimum coverage percentage in CI with `covr::percent_coverage`
- Running property-based tests with `hedgehog` to verify algebraic laws like commutativity or idempotence

---

## testthat 3e — Modern Syntax

```r
# tests/testthat/test-compute.R
library(testthat)

test_that("compute_mean returns correct trimmed mean", {
  expect_equal(compute_mean(c(1, 2, 3, 4, 5)), 3)
  expect_equal(compute_mean(c(1, 2, 3, 100), trim = 0.25), 2.5)
})

test_that("compute_mean removes NA by default", {
  result <- compute_mean(c(1, NA, 3))
  expect_equal(result, 2)
})

test_that("compute_mean errors on non-numeric input", {
  expect_error(
    compute_mean(c("a", "b")),
    regexp = "must be numeric"
  )
})

test_that("compute_mean returns NaN for empty vector", {
  expect_true(is.nan(compute_mean(numeric(0))))
})
```

### Assertion Reference

```r
# Equality
expect_equal(actual, expected)           # numeric tolerance applied
expect_identical(actual, expected)       # exact, no tolerance
expect_equivalent(actual, expected)      # ignores attributes

# Type checks
expect_type(x, "double")
expect_s3_class(df, "data.frame")
expect_s4_class(m, "Matrix")
expect_inherits(x, "tbl_df")

# Conditions
expect_error(expr, regexp = NULL)        # regexp: optional pattern
expect_warning(expr, regexp = NULL)
expect_message(expr, regexp = NULL)
expect_no_error(expr)
expect_no_warning(expr)

# Logical
expect_true(cond)
expect_false(cond)
expect_null(x)
expect_length(x, n)

# Data frames
expect_equal(nrow(df), 5L)
expect_named(df, c("id", "name", "value"))
expect_contains(names(df), "created_at")

# Snapshots
expect_snapshot(print(my_object))        # console output snapshot
expect_snapshot_file(path, "chart.png")  # file snapshot
```

---

## Snapshot Testing

Snapshot tests capture output and fail if it changes unexpectedly.

```r
test_that("user_summary prints correctly", {
  user <- new_user(id = 1L, name = "Alice", email = "alice@example.com")
  expect_snapshot(print(user))
})

# Update snapshots after intentional change:
# testthat::snapshot_review()
# testthat::snapshot_accept()
```

---

## Mocking with mockery

```r
library(mockery)

test_that("fetch_data calls correct URL", {
  mock_response <- list(
    status_code = 200L,
    content     = list(id = 1L, name = "Alice")
  )

  stub(fetch_user, "httr::GET", mock(mock_response))

  result <- fetch_user(id = 1L)
  expect_equal(result$name, "Alice")
})

test_that("fetch_data handles 404", {
  stub(fetch_user, "httr::GET", mock(list(status_code = 404L)))

  expect_error(fetch_user(id = 999L), "not found")
})
```

---

## Mocking HTTP with httptest2

```r
library(httptest2)

test_that("GET /users/1 returns user data", {
  with_mock_api({
    result <- get_user(1L)
    expect_equal(result$name, "Alice")
  })
})

# Record real API responses for replay:
# httptest2::capture_requests({ get_user(1L) })
# Saves to tests/testthat/api.example.com/users/1.json
```

---

## Code Coverage with covr

```r
# Run package coverage
cov <- covr::package_coverage()

# Print summary
covr::zero_coverage(cov)   # show uncovered lines
print(cov)

# HTML report
covr::report(cov)

# Enforce minimum in CI
min_coverage <- 80
pct <- covr::percent_coverage(cov)
if (pct < min_coverage) {
  stop(sprintf("Coverage %.1f%% below minimum %.0f%%", pct, min_coverage))
}
```

---

## Shiny App Testing with shinytest2

```r
library(shinytest2)

test_that("user input triggers correct output", {
  app <- AppDriver$new(app_dir = ".", name = "my_app")

  app$set_inputs(user_id = 1)
  app$click("btn_fetch")
  app$wait_for_idle()

  output <- app$get_value(output = "user_name")
  expect_equal(output, "Alice")

  app$stop()
})
```

---

## Property-Based Testing with hedgehog

```r
library(hedgehog)

test_that("sum is commutative (property)", {
  forall(
    list(gen.int(100), gen.int(100)),
    function(a, b) expect_equal(a + b, b + a)
  )
})

test_that("sort is idempotent (property)", {
  forall(
    gen.element(list(gen.c(gen.double()), gen.c(gen.int(50)))),
    function(x) expect_equal(sort(sort(x)), sort(x))
  )
})
```

---

## Test File Organization

```
my_package/
  R/
    compute.R
    fetch.R
    shiny_app.R
  tests/
    testthat/
      test-compute.R
      test-fetch.R
      test-shiny.R
      fixtures/
        user_1.json
      _snaps/
        user_summary.md
    testthat.R
  DESCRIPTION
```

---

## Running Tests

```r
# All tests
devtools::test()

# Single file
testthat::test_file("tests/testthat/test-compute.R")

# Specific test by pattern
devtools::test(filter = "compute")

# With coverage
covr::package_coverage()
```

Related Skills

visual-testing

8
from marvinrichter/clarc

Visual Regression Testing: tool comparison (Chromatic/Percy/Playwright screenshots/BackstopJS), pixel-diff vs AI-based comparison, baseline management, flakiness strategies (masks, tolerances, waitForLoadState), CI integration with GitHub Actions, and Storybook integration.

typescript-testing

8
from marvinrichter/clarc

TypeScript testing patterns: Vitest for unit/integration, Playwright for E2E, MSW for API mocking, Testing Library for React components. Core TDD methodology for TypeScript/JavaScript projects.

swift-testing

8
from marvinrichter/clarc

Swift testing patterns: Swift Testing framework (Swift 6+), XCTest for UI tests, async/await test cases, actor testing, Combine testing, and XCUITest for UI automation. TDD for Swift/SwiftUI.

swift-protocol-di-testing

8
from marvinrichter/clarc

Protocol-based dependency injection for testable Swift code — mock file system, network, and external APIs using focused protocols and Swift Testing.

scala-testing

8
from marvinrichter/clarc

Scala testing with ScalaTest, MUnit, and ScalaCheck: FunSpec/FlatSpec test structure, property-based testing with forAll, mocking with MockitoSugar, Cats Effect testing with munit-cats-effect (runTest/IOSuite), ZIO Test, Testcontainers-Scala for database integration tests, and CI integration with sbt. Use when writing or reviewing Scala tests.

rust-testing

8
from marvinrichter/clarc

Rust testing patterns — unit tests with mockall, integration tests with sqlx transactions, HTTP handler testing (axum), benchmarks (criterion), property tests (proptest), fuzzing, and CI with cargo-nextest.

rust-testing-advanced

8
from marvinrichter/clarc

Advanced Rust testing anti-patterns and corrections — cfg(test) placement, expect() over unwrap(), mockall expectation ordering, executor mixing (#[tokio::test] vs block_on), PgPool isolation with

ruby-testing

8
from marvinrichter/clarc

RSpec testing patterns for Ruby and Rails — factories, mocks, request specs, feature specs, VCR, and SimpleCov coverage.

python-testing

8
from marvinrichter/clarc

Python testing strategies using pytest, TDD methodology, fixtures, mocking, and parametrization. Core testing fundamentals.

python-testing-advanced

8
from marvinrichter/clarc

Advanced Python testing — async testing with pytest-asyncio, exception/side-effect testing, test organization, common patterns (API, database, class methods), pytest configuration, and CLI reference. Extends python-testing.

php-testing

8
from marvinrichter/clarc

PHP testing patterns: PHPUnit 11 with mocks and data providers, Pest v3 with expectations and datasets, Laravel feature/HTTP tests with RefreshDatabase, Symfony WebTestCase, PHPStan static analysis, Infection mutation testing. Use when writing or reviewing PHP tests.

load-testing

8
from marvinrichter/clarc

Load and performance testing with k6 (TypeScript/Go/any HTTP) and Locust (Python). Covers test types (smoke, load, stress, spike, soak), SLO thresholds, CI integration, and interpreting results.