R
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Data Structures
-
Section 4: Data Manipulation
-
Section 5: Visualization and Statistics
-
Section 6: Advanced R
-
Section 7: Practical Projects
-
Section 8: Interview Practice
-
Section 9: More Practice Exercises
-
Section 10: Shiny Apps in Depth
-
Section 11: More Data Wrangling
-
Section 12: Tidyverse Deep Dive
-
Section 13: Statistical Modeling Deep Dive
-
Section 14: Machine Learning in R
-
Section 15: R Visualization Deep Dive
-
Section 16: R Package Development Deep Dive
-
Section 17: R for Reproducible Research
-
Section 18: R and Databases
-
Section 19: R Performance Optimization
-
Section 20: Bioinformatics and Specialized R
-
Section 21: More Shiny Practice
-
Section 22: More Practice Exercises
-
Section 23: R Data Structures Deep Dive
-
Section 24: More Interview and Review
-
Section 25: R for Business Analytics
-
Section 26: R Text Mining and NLP
-
Section 27: R Spatial Data Analysis
-
Section 28: R Deep Learning
-
Section 29: Advanced Statistical Techniques
-
Section 30: R Object Systems Deep Dive
-
Section 31: R Environments and Metaprogramming
-
Section 32: R for Finance
-
Section 33: R for Clinical and Health Data
-
Section 34: More Shiny Advanced Practice
-
Section 35: R Data Cleaning Deep Dive
-
Section 36: R Reporting Automation
-
Section 37: More Practical Projects Round 2
-
Section 38: R Ecosystem and Career
-
Section 39: More Visualization Practice
-
Section 40: R for Experimentation
-
Section 41: R for Genomics and Bioinformatics Deep Dive
-
Section 42: R for Marketing Analytics
-
Section 43: R Data Import/Export Deep Dive
-
Section 44: R String Processing Deep Dive
-
Section 45: R for Actuarial and Insurance Analytics
-
Section 46: R Testing and Quality Assurance Deep Dive
-
Section 47: R Version Control and Collaboration
-
Section 48: R Advanced Functional Programming
-
Section 49: R for Supply Chain and Operations
-
Section 50: More Practice Exercises Round 3
-
Section 51: R Dashboards and BI Integration
-
Section 52: R Data Governance and Ethics
-
Section 53: More Modeling Practice
-
Section 54: R Final Capstone Projects
-
Section 55: R for Sports Analytics
-
Section 56: More Interview Practice Round 2
-
Section 57: R Networking and APIs
-
Section 58: R for Environmental Science
-
Section 59: R Notebook and Documentation Practices
-
Section 60: More Data Wrangling Mastery
-
Section 61: R for A/B Testing at Scale
-
Section 62: R Package Ecosystem Deep Dive
184: Code Coverage with covr
I’ve spent a lot of my career thinking that if my tests pass, my code is "done." It's a dangerous assumption. You can have a suite of 50 tests that all pass with flying colors, but if those tests only exercise the "happy path," you're essentially flying blind regarding the edge cases where the real bugs usually hide.
Let's look at a small package I've been tinkering with. It has a function designed to calculate a performance bonus for a sales team. I wrote a few tests, and they're all green. But I have a nagging feeling that I've missed a logic branch.
The "Perfect" Test Suite
Here is the function I'm working with:
calculate_bonus <- function(sales, tenure_years) {
if (tenure_years < 1) {
return(0) # New hires don't get bonuses
}
if (sales > 10000) {
return(sales * 0.10) # High performers get 10%
} else {
return(sales * 0.02) # Everyone else gets 2%
}
}
And here is the test I wrote for it in my tests/testthat/test-bonus.R file:
test_that("high performers get the correct bonus", {
expect_equal(calculate_bonus(15000, 2), 1500)
})
I run devtools::test(), it says 1 test passed, and I'm tempted to call it a day. But this is where covr comes in. Instead of guessing, I want to see exactly which lines of code were actually executed during that test run.
Seeing the Gaps
I'll pull in the covr package and run the coverage report on my package. Since I'm developing this as a package, I can use the package_coverage() function.
# I'll run this from the console
covr::package_coverage()
When I run this, R generates a report (often opening in the viewer or a browser). Looking at the output, I see a percentage—likely around 40-50%—and when I click into the source code view, I see some lines highlighted in green and others in red. The red lines are the ones the tests never touched.
Wait, look at the tenure_years < 1 block. It's red. And the else block for the 2% bonus? Also red. My "passing" test only exercised the high-performer branch for experienced employees. I completely ignored new hires and average performers.
Filling the Holes
This is the "aha" moment. covr isn't telling me my code is wrong; it's telling me my tests are incomplete. I can't claim the function works for new hires if I've never actually run the code that handles new hires.
I'll go back to my test file and add cases to hit those red lines:
test_that("bonus logic handles all scenarios", {
# Testing the new hire branch (the first red block)
expect_equal(calculate_bonus(20000, 0.5), 0)
# Testing the low-performance branch (the second red block)
expect_equal(calculate_bonus(5000, 2), 100)
})
Now, I run covr::package_coverage() again. I refresh the report, and suddenly, the whole function is green. 100% coverage. Now I can actually sleep at night knowing that every logical branch in that function has been executed at least once.
The Trap of 100% Coverage
Now, a quick word of caution from someone who has crashed a few production servers: 100% coverage does not mean 100% correctness. Coverage tells you that a line was executed, not that it was executed with every possible weird input. For example, I haven't tested what happens if sales is a negative number or a string. covr tells me I've visited every room in the house, but it doesn't tell me if the furniture in those rooms is actually bolted down correctly.
Use covr to find the "dark corners" of your code that aren't being tested at all, but keep using your engineering intuition to write meaningful assertions.
📋 Practical Task
Closing the Coverage Gap in a Discount Calculator
You have a function called apply_discount that applies a discount based on customer loyalty and purchase amount. However, your current test suite is only hitting part of the logic.
apply_discount <- function(amount, is_loyal) {
if (amount < 0) {
stop("Amount cannot be negative")
}
if (is_loyal) {
if (amount > 100) {
return(amount * 0.80) # 20% off
} else {
return(amount * 0.90) # 10% off
}
} else {
return(amount) # No discount for non-loyal customers
}
}
Your current test is:
test_that("loyal customers with high spend get 20% off", {
expect_equal(apply_discount(200, TRUE), 160)
})
Your Task: Use the covr philosophy to identify the uncovered branches. Write a set of additional test_that blocks that ensure 100% code coverage for the apply_discount function, including the error handling for negative amounts.
There are no comments for now.