takeokunn

Testing Patterns

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.

takeokunn 68 1 Updated 3mo ago
GitHub

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