/tdd-workflow
Test-driven development workflow for R using testthat. Use when writing new features, fixing bugs, or refactoring code. Enforces test-first development with 80%+ coverage.
$ npx -y skills add ab604/claude-code-r-skills --skill tdd-workflow --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
- Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
- You can call itInvoke it directly when you want it.
- Slash command
/tdd-workflow
Context preview
The summary Claude sees to decide when to auto-load this skill.
Test-driven development workflow for R using testthat. Use when writing new features, fixing bugs, or refactoring code. Enforces test-first development with 80%+ coverage.
SKILL.md
tdd-workflow.SKILL.mdname: tdd-workflow
description: Test-driven development workflow for R using testthat. Use when writing new features, fixing bugs, or refactoring code. Enforces test-first development with 80%+ coverage.
Test-Driven Development Workflow for R
This skill ensures all R code development follows TDD principles with comprehensive test coverage using testthat.
When to Activate
- Writing new functions or features
- Fixing bugs or issues
- Refactoring existing code
- Adding new model types
- Creating data processing pipelines
- Building Shiny components
Getting Started
Initialize testing infrastructure for your package:
# Set up testthat (Edition 3)
usethis::use_testthat(3)
# Create a test file for an existing source file
usethis::use_test("function_name")
# Or create test and source file together
usethis::use_r("function_name")
usethis::use_test("function_name")Core Principles
1. Tests BEFORE Code
ALWAYS write tests first, then implement code to make tests pass.
2. Coverage Requirements
- Minimum 80% coverage (unit + integration)
- 100% coverage for statistical calculations
- 100% coverage for data validation
- All edge cases covered
- Error scenarios tested
3. Test Types
Tests follow a three-level hierarchy: **File → Test → Expectation**
Unit Tests
Individual functions and utilities:
test_that("rescale01 normalizes to [0, 1] range", {
expect_equal(rescale01(c(0, 5, 10)), c(0, 0.5, 1))
expect_equal(rescale01(c(-10, 0, 10)), c(0, 0.5, 1))
})
test_that("rescale01 handles edge cases", {
expect_equal(rescale01(c(5, 5, 5)), c(NaN, NaN, NaN))
expect_equal(rescale01(numeric(0)), numeric(0))
expect_equal(rescale01(c(0, NA, 10)), c(0, NA, 1))
})Integration Tests
Function interactions and workflows:
test_that("data pipeline produces expected output", {
raw_data <- read_fixture("sample_input.csv")
result <- raw_data |>
clean_data() |>
transform_features() |>
summarize_results()
expect_s3_class(result, "tbl_df")
expect_named(result, c("group", "mean", "sd", "n"))
expect_true(all(result$n > 0))
})Snapshot Tests
For complex outputs that are hard to specify:
test_that("model summary format is stable", {
model <- fit_model(test_data)
expect_snapshot(print(summary(model)))
})
test_that("error messages are informative", {
expect_snapshot(
validate_input(invalid_data),
error = TRUE
)
})**Snapshot workflow:**
# Review snapshot changes
testthat::snapshot_review("test_name")
# Accept snapshot changes
testthat::snapshot_accept("test_name")Snapshots are stored in `tests/testthat/_snaps/` directory.
BDD Alternative (Optional)
For behavior-driven development, use `describe()` and `it()`:
describe("matrix()", {
it("can be multiplied by a scalar", {
m1 <- matrix(1:4, 2, 2)
m2 <- m1 * 2
expect_equal(matrix(c(2, 4, 6, 8), 2, 2), m2)
})
it("can be transposed", {
m <- matrix(1:4, 2, 2)
expect_equal(t(m), matrix(c(1, 3, 2, 4), 2, 2))
})
})**Key distinction:** "describe() verifies you implement the right things, test_that() ensures you do things right."
Test Design Principles
Self-Sufficient Tests
Each test should contain all setup, execution, and teardown code. Tests must be independent and runnable in isolation without relying on ambient state or prior test execution.
# GOOD: Self-contained
test_that("function works with specific data", {
data <- tibble(x = 1:10, y = rnorm(10)) # Setup
result <- my_function(data) # Execute
expect_equal(nrow(result), 10) # Assert
})
# BAD: Depends on external state
# setup_data <- tibble(...) # Created outside test
test_that("function works", {
result <- my_function(setup_data) # Relies on external data
expect_equal(nrow(result), 10)
})Duplication Over Factoring
Repetition is acceptable in tests—duplicate setup code rather than extracting it elsewhere. Clarity outweighs avoiding duplication.
# GOOD: Duplicated but clear
test_that("clean_data handles missing values", {
data <- tibble(x = c(1, NA, 3), y = c(4, 5, 6))
result <- clean_data(data)
expect_equal(nrow(result), 2)
})
test_that("clean_data handles invalid values", {
data <- tibble(x = c(1, -999, 3), y = c(4, 5, 6))
result <- clean_data(data, invalid = -999)
expect_equal(nrow(result), 2)
})
# ACCEPTABLE: Each test is self-contained and readablePlan for Failure
Write tests assuming they'll fail and require debugging. Make logic explicit and obvious. Run tests in fresh R sessions independently.
Use devtools::load_all()
During development, prefer `devtools::load_all()` over `library()`. This:
- Exposes unexported functions for testing
- Automatically attaches testthat
- Eliminates unnecessary `library()` calls in tests
- Simulates package loading without installation
testthat Edition 3
Edition 3 provides improved snapshot testing, better diffs via waldo, unified condition handling, parallel execution support, and byte-compiled code compatibility for mocking.
Deprecated Patterns → Modern Alternatives
# DEPRECATED: context() calls
context("Data validation") # Remove - filename serves this purpose
# DEPRECATED: expect_equivalent()
expect_equivalent(x, y)
# MODERN:
expect_equal(x, y, ignore_attr = TRUE)
# DEPRECATED: with_mock()
with_mock(external_call = function() "mocked", {
result <- my_function()
})
# MODERN:
local_mocked_bindings(
external_call = function() "mocked"
)
result <- my_function()
# DEPRECATED: expect_is()
expect_is(x, "data.frame")
# MODERN:
expect_s3_class(x, "data.frame")Initialize Edition 3
In `DESCRIPTION`, ensure:
Config/testthat/edition: 3
Or initialize with:
usethis::use_testthat(3)
Essential Expectations Reference
Equality & Identity
expect_equal(x, y) # With numeric tolerance
expect_equal(x, y, tolerance = 0.001)
expect
Read more
name: tdd-workflow description: Test-driven development workflow for R using testthat. Use when writing new features, fixing bugs, or refactoring code. Enforces test-first development with 80%+ coverage.
Test-Driven Development Workflow for R
This skill ensures all R code development follows TDD principles with comprehensive test coverage using testthat.
When to Activate
- Writing new functions or features
- Fixing bugs or issues
- Refactoring existing code
- Adding new model types
- Creating data processing pipelines
- Building Shiny components
Getting Started
Initialize testing infrastructure for your package:
# Set up testthat (Edition 3)
usethis::use_testthat(3)
# Create a test file for an existing source file
usethis::use_test("function_name")
# Or create test and source file together
usethis::use_r("function_name")
usethis::use_test("function_name")Core Principles
1. Tests BEFORE Code
ALWAYS write tests first, then implement code to make tests pass.
2. Coverage Requirements
- Minimum 80% coverage (unit + integration)
- 100% coverage for statistical calculations
- 100% coverage for data validation
- All edge cases covered
- Error scenarios tested
3. Test Types
Tests follow a three-level hierarchy: **File → Test → Expectation**
Unit Tests
Individual functions and utilities:
test_that("rescale01 normalizes to [0, 1] range", {
expect_equal(rescale01(c(0, 5, 10)), c(0, 0.5, 1))
expect_equal(rescale01(c(-10, 0, 10)), c(0, 0.5, 1))
})
test_that("rescale01 handles edge cases", {
expect_equal(rescale01(c(5, 5, 5)), c(NaN, NaN, NaN))
expect_equal(rescale01(numeric(0)), numeric(0))
expect_equal(rescale01(c(0, NA, 10)), c(0, NA, 1))
})Integration Tests
Function interactions and workflows:
test_that("data pipeline produces expected output", {
raw_data <- read_fixture("sample_input.csv")
result <- raw_data |>
clean_data() |>
transform_features() |>
summarize_results()
expect_s3_class(result, "tbl_df")
expect_named(result, c("group", "mean", "sd", "n"))
expect_true(all(result$n > 0))
})Snapshot Tests
For complex outputs that are hard to specify:
test_that("model summary format is stable", {
model <- fit_model(test_data)
expect_snapshot(print(summary(model)))
})
test_that("error messages are informative", {
expect_snapshot(
validate_input(invalid_data),
error = TRUE
)
})**Snapshot workflow:**
# Review snapshot changes
testthat::snapshot_review("test_name")
# Accept snapshot changes
testthat::snapshot_accept("test_name")Snapshots are stored in `tests/testthat/_snaps/` directory.
BDD Alternative (Optional)
For behavior-driven development, use `describe()` and `it()`:
describe("matrix()", {
it("can be multiplied by a scalar", {
m1 <- matrix(1:4, 2, 2)
m2 <- m1 * 2
expect_equal(matrix(c(2, 4, 6, 8), 2, 2), m2)
})
it("can be transposed", {
m <- matrix(1:4, 2, 2)
expect_equal(t(m), matrix(c(1, 3, 2, 4), 2, 2))
})
})**Key distinction:** "describe() verifies you implement the right things, test_that() ensures you do things right."
Test Design Principles
Self-Sufficient Tests
Each test should contain all setup, execution, and teardown code. Tests must be independent and runnable in isolation without relying on ambient state or prior test execution.
# GOOD: Self-contained
test_that("function works with specific data", {
data <- tibble(x = 1:10, y = rnorm(10)) # Setup
result <- my_function(data) # Execute
expect_equal(nrow(result), 10) # Assert
})
# BAD: Depends on external state
# setup_data <- tibble(...) # Created outside test
test_that("function works", {
result <- my_function(setup_data) # Relies on external data
expect_equal(nrow(result), 10)
})Duplication Over Factoring
Repetition is acceptable in tests—duplicate setup code rather than extracting it elsewhere. Clarity outweighs avoiding duplication.
# GOOD: Duplicated but clear
test_that("clean_data handles missing values", {
data <- tibble(x = c(1, NA, 3), y = c(4, 5, 6))
result <- clean_data(data)
expect_equal(nrow(result), 2)
})
test_that("clean_data handles invalid values", {
data <- tibble(x = c(1, -999, 3), y = c(4, 5, 6))
result <- clean_data(data, invalid = -999)
expect_equal(nrow(result), 2)
})
# ACCEPTABLE: Each test is self-contained and readablePlan for Failure
Write tests assuming they'll fail and require debugging. Make logic explicit and obvious. Run tests in fresh R sessions independently.
Use devtools::load_all()
During development, prefer `devtools::load_all()` over `library()`. This:
- Exposes unexported functions for testing
- Automatically attaches testthat
- Eliminates unnecessary `library()` calls in tests
- Simulates package loading without installation
testthat Edition 3
Edition 3 provides improved snapshot testing, better diffs via waldo, unified condition handling, parallel execution support, and byte-compiled code compatibility for mocking.
Deprecated Patterns → Modern Alternatives
# DEPRECATED: context() calls
context("Data validation") # Remove - filename serves this purpose
# DEPRECATED: expect_equivalent()
expect_equivalent(x, y)
# MODERN:
expect_equal(x, y, ignore_attr = TRUE)
# DEPRECATED: with_mock()
with_mock(external_call = function() "mocked", {
result <- my_function()
})
# MODERN:
local_mocked_bindings(
external_call = function() "mocked"
)
result <- my_function()
# DEPRECATED: expect_is()
expect_is(x, "data.frame")
# MODERN:
expect_s3_class(x, "data.frame")Initialize Edition 3
In `DESCRIPTION`, ensure:
Config/testthat/edition: 3
Or initialize with:
usethis::use_testthat(3)
Essential Expectations Reference
Equality & Identity
expect_equal(x, y) # With numeric tolerance expect_equal(x, y, tolerance = 0.001) expect
A curated collection of Claude Code configurations for modern R use. These skills, rules, commands, and agents help Claude Code understand R best practices and generate idiomatic, high-quality R code.
Repo: ab604/claude-code-r-skills
Other skills on r-skills.
- /r-bayes
Patterns for Bayesian inference in R using brms, including multilevel models, DAG validation, and marginal effects. Use when performing Bayesian analysis.
Open skill - /r-oop
R object-oriented programming guide for S7, S3, S4, and vctrs. Use when designing R classes or choosing an OOP system.
Open skill - /r-package-development
R package development guide covering dependencies, API design, testing, and documentation. Use when developing R packages.
Open skill - /r-performance
R performance best practices including profiling, benchmarking, vctrs, and optimization strategies. Use when optimizing R code.
Open skill - /r-style-guide
R style guide covering naming conventions, spacing, layout, and function design best practices. Use when writing R code.
Open skill - /rlang-patterns
rlang metaprogramming patterns for data-masking, injection operators, and dynamic dots. Use when writing functions that use tidy evaluation.
Open skill

