Testing philosophy and practices. This skill should be used when writing tests, designing test strategies, or reviewing test code. Use proactively when discussing TDD, red-green-refactor, test doubles, mocks, stubs, fakes, RSpec, Jest, pytest, unit tests, integration tests, test coverage, or test-first development. (user)
This skill provides guidance on testing philosophy and practices, emphasizing tests as specifications and API design through TDD.
Tests are not just verification tools — they are executable specifications that document how the system should behave. A well-written test suite serves as living documentation.
Tests are the first users of your code's APIs. This is why TDD is valuable: you design the API by thinking about the consumer first, before thinking about implementation.
When writing tests:
The TDD cycle consists of three phases:
Each cycle should be short — ideally minutes, not hours. Small steps reduce risk and provide frequent feedback.
Strict TDD (one test at a time, red-green-refactor) is the ideal for learning and for complex logic. However, flexibility is acceptable:
Writing all tests first is appropriate when:
Writing tests after is acceptable when:
The goal is well-tested code with tests that serve as specifications. The path matters less than the destination, but TDD often produces better results.
Tests should be fast. Slow tests discourage running them frequently, which defeats their purpose.
Avoid hitting the database in tests except when:
Do not hit the database just to:
Use factories or builders that create in-memory objects when database persistence isn't the thing being tested.
Each test should verify one behavior. This doesn't always mean one assertion — sometimes verifying one behavior requires multiple assertions, especially when tests are slow. But the test should have a single reason to fail.
Structure tests using Arrange-Act-Assert:
Keep each section clearly delineated. If any section is complex, consider extracting helper methods.
The BDD mindset aligns with AAA:
This framing helps focus on behavior from the user's perspective.
Avoid mocking when possible. Build small, simple components with immutable data to reduce the need for mocks.
If mocking is unavoidable:
Consider these as signals to refactor the production code.
Use custom matchers (RSpec matchers, Jest matchers, etc.) to make assertions readable and intention-revealing.
Good:
expect(order).to be_fulfilled
expect(user).to have_permission(:admin)
Less clear:
expect(order.status).to eq("fulfilled")
expect(user.permissions).to include("admin")
Custom matchers:
describe for classes/methods, context for states/conditionslet for lazy-evaluated test datasubject for the thing being testedexpect syntax over shouldbefore sparingly; prefer explicit setup in each test when clarity mattersshared_examples for common behavior across contextsbuild over create when persistence isn't neededRSpec.describe Order do
describe "#fulfill" do
context "when all items are in stock" do
it "marks the order as fulfilled" do
order = build(:order, :with_available_items)
order.fulfill
expect(order).to be_fulfilled
end
end
end
end
describe blocks to group related testsbeforeEach for common setupdescribe("Order", () => {
describe("fulfill", () => {
it("marks the order as fulfilled when all items are in stock", () => {
const order = buildOrder({ items: availableItems });
order.fulfill();
expect(order.isFulfilled()).toBe(true);
});
});
});
Watch for these warning signs:
Search for places (restaurants, cafes, etc.) via Google Places API proxy on localhost.
Interact with GitHub using the `gh` CLI. Use `gh issue`, `gh pr`, `gh run`, and `gh api` for issues, PRs, CI runs, and advanced queries.
Create or update AgentSkills. Use when designing, structuring, or packaging skills with scripts, references, and assets.
Start voice calls via the OpenClaw voice-call plugin.
Notion API for creating and managing pages, databases, and blocks.
Gemini CLI for one-shot Q&A, summaries, and generation.
Category:developer