This skill should be used when the user asks to "write tests", "test strategy", "coverage", "unit test", "integration test", or needs testing guidance. Provides testing methodology and patterns.
Install
npx skillscat add takeokunn/nixos-configuration/testing-patterns Install via the SkillsCat registry.
SKILL.md
Provide testing patterns and strategies for comprehensive test coverage and maintainable test suites.
Test individual functions/methods in isolation
Single function, class, or module
Fast, isolated, deterministic
Business logic, utility functions, transformations
Test interaction between components
Multiple components working together
Slower, may use real dependencies
API endpoints, database operations, service interactions
Test complete user workflows
Full application stack
Slowest, tests real user scenarios
Critical user journeys, smoke tests
Three-phase test structure for clear test organization
Are you writing unit or integration tests?
Apply arrange-act-assert pattern for clear test structure</if_yes>
Consider given-when-then for BDD-style tests</if_no>
</decision_tree>
Arrange: Set up test data and preconditions</test_phase>
user = User.new(name: "John")
cart = ShoppingCart.new(user)
<test_phase>Act: Execute the code under test</test_phase>
total = cart.calculate_total
<test_phase>Assert: Verify expected outcomes</test_phase>
assert_equal 0, total
Separates setup, execution, and verification into distinct phases
BDD-style test structure focusing on behavior
Is the test focused on business behavior rather than technical implementation?
Apply given-when-then pattern for BDD-style tests</if_yes>
Use arrange-act-assert for technical unit tests</if_no>
</decision_tree>
Given: Initial context (preconditions)</bdd_step>
given_a_user_with_an_empty_cart
<bdd_step>When: Action or trigger</bdd_step>
when_the_user_calculates_total
<bdd_step>Then: Expected outcome</bdd_step>
then_the_total_should_be_zero
Emphasizes business behavior over technical implementation
Provide canned responses for dependencies
Does the test need dependency responses but not interaction verification?
Apply stub pattern for canned responses</if_yes>
Use mock if interaction verification is needed</if_no>
</decision_tree>
api_client = stub(
fetch_user: { id: 1, name: "John" }
)
Replace slow/unreliable dependencies</use_case>
Verify interactions occurred with dependencies
Does the test need to verify specific interactions occurred?
Apply mock pattern to verify method calls and arguments</if_yes>
Use stub if only canned responses are needed</if_no>
</decision_tree>
email_service = mock()
email_service.expect(:send_email, args: ["user@example.com", "Welcome"])
user_service.register(email_service)
email_service.verify
Ensure methods called with correct arguments</use_case>
Record calls while using real implementation
Does the test need real behavior plus interaction verification?
Apply spy pattern to record calls while using real implementation</if_yes>
Use stub for canned responses or mock for behavior replacement</if_no>
</decision_tree>
logger = spy(Logger.new)
service.process(logger)
assert_called logger, :log, with: "Processing complete"
Verify side effects without changing behavior</use_case>
Working implementation suitable for testing
Does the test need a simplified but working implementation?
Apply fake pattern for lightweight working implementation</if_yes>
Use stub for simple canned responses</if_no>
</decision_tree>
class FakeDatabase
def initialize
@data = {}
end
def save(key, value)
@data[key] = value
end
def find(key)
@data[key]
end
end
In-memory database, fake file system</use_case>
Test names that clearly describe scenario and outcome
Is this a technical unit test for a specific method?
Apply descriptive naming with method-scenario-result format</if_yes>
Consider should naming for BDD-style tests</if_no>
</decision_tree>
test_calculateTotal_withEmptyCart_returnsZero
test_calculateTotal_withMultipleItems_returnsSumOfPrices
test_calculateTotal_withDiscount_appliesDiscountCorrectly
Format: test_[method]_[scenario]_[expected_result]
BDD-style naming that reads like natural language
Is this a behavior-focused test readable by non-technical stakeholders?
Apply should naming for natural language readability</if_yes>
Use descriptive naming for technical unit tests</if_no>
</decision_tree>
calculateTotal_should_returnZero_when_cartIsEmpty
calculateTotal_should_applyDiscount_when_couponIsValid
calculateTotal_should_throwError_when_pricesAreNegative
Format: [method]_should_[expected_behavior]_when_[condition]
Test happy path first
Start with the normal, expected flow before edge cases
test_userLogin_withValidCredentials_succeeds
test_userLogin_withInvalidPassword_fails
test_userLogin_withLockedAccount_fails
Test edge cases
Test boundary conditions and limits
Empty inputs, maximum values, null values, zero values, negative numbers
Test error cases
Verify error handling paths work correctly
Invalid inputs, network failures, permission errors, timeout scenarios
Isolate tests
Each test should be independent
Use setup/teardown to reset state
def setup
@database = TestDatabase.new
@service = UserService.new(@database)
end
def teardown
@database.clear
end
</example>
Make tests readable
Tests serve as documentation
Good: Clear and descriptive
test_userRegistration_withExistingEmail_returnsError
<note>Bad: Unclear purpose</note>
test_user_reg_1
</example>
One assertion per concept
Each test should verify one logical concept
Good: Single concept
test_userCreation_setsDefaultRole
user = create_user
assert_equal "member", user.role
end
<note>Avoid: Multiple unrelated assertions</note>
test_userCreation
user = create_user
assert_equal "member", user.role
assert_not_nil user.email
assert_true user.active
end
</example>
Use test fixtures and factories
Extract common test data setup
Create reusable test data
def create_test_user(overrides = {})
defaults = {
name: "Test User",
email: "test@example.com",
role: "member"
}
User.new(defaults.merge(overrides))
end
Avoid magic numbers
Use named constants for test values
Good</good_example>
VALID_USER_AGE = 25
MINIMUM_AGE = 18
test_userValidation_withValidAge_succeeds
user = User.new(age: VALID_USER_AGE)
assert user.valid?
end
<bad_example>Bad</bad_example>
test_userValidation_withValidAge_succeeds
user = User.new(age: 25)
assert user.valid?
end
</example>
Test corner cases
Test unusual combinations and scenarios
Concurrent access, timezone edge cases, leap years, DST transitions
</best_practices>
Percentage of code lines executed during tests
Measures which lines of code are exercised
Percentage of code branches (if/else, switch) taken during tests
More thorough than line coverage as it measures decision paths
Percentage of functions/methods called during tests
Identifies untested functions
Aim for high coverage but prioritize meaningful tests over coverage numbers
80%+ coverage is a good target for critical code paths
100% coverage does not guarantee bug-free code
Focus on testing behavior, not achieving coverage metrics
Use to define test requirements and acceptance criteria
Use to implement tests as part of feature development workflow
Use when debugging test failures or flaky tests
</related_skills>
Testing implementation details instead of behavior
Focus on testing observable behavior and outcomes, not internal implementation details. Test what the code does, not how it does it.
Over-mocking dependencies throughout test suites
Use real implementations where practical; excessive mocking often indicates poor design. Only mock external dependencies or slow operations.
Tests that sometimes pass and sometimes fail
Ensure tests are deterministic by controlling time, randomness, and async operations. Use fixed timestamps, seeded random generators, and proper async handling.
Tests that take too long to run
Use unit tests for fast feedback; reserve slow integration/e2e tests for critical paths. Unit tests should run in milliseconds, not seconds.
Tests that depend on execution order or shared state
Make each test independent with proper setup/teardown and isolated state. Each test should create its own test data.
</anti_patterns>
Minor coverage gap in non-critical path
Note in report, proceed
Test flakiness detected
Document issue, use AskUserQuestion for clarification
Critical path lacks test coverage
STOP, present options to user
Tests reveal security vulnerability
BLOCK operation, require explicit user acknowledgment
</error_escalation>
Follow project test patterns
Run tests after creation
Cover critical paths first
Creating tests without understanding implementation
Writing flaky or non-deterministic tests
Ignoring existing test conventions