Token导航 LogoToken导航TokenDH.com
研究检索external-serviceclawhub未标认证来源可访问clear审计通过

bookforge-build-refactoring-test-suitebookforge 构建重构测试套件

Agent Skill

用于辅助测试设计、自动化测试、用例整理和回归验证。它适合让 Agent 编写单元测试、端到端测试、测试计划或根据失败日志定位问题。使用时需要确认项目测试框架、运行命令和夹具数据,避免为了通过测试而改坏真实逻辑;涉及浏览器或外部服务时,应区分本地模拟、测试环境和生产环境。

总安装

4,093

周安装

164

GitHub Stars

公开资料未说明

下载量

1,325
OpenClaw

安装说明

本站只整理中文说明和来源信息,不托管安装包,也不代用户安装。

GitHub

来源数

2

许可证

MIT-0

最后核验

2026-05-01

来源状态

来源可访问

安装方式

通过对话安装

复制提示词发给支持本地命令或 Skills 的 AI 助手,先确认命令和权限,再让它执行。

请帮我安装这个 Agent Skill:bookforge-build-refactoring-test-suite(bookforge 构建重构测试套件)
来源仓库:https://github.com/quochungto/bookforge-build-refactoring-test-suite
安装命令:
openclaw skills install bookforge-build-refactoring-test-suite
安装前请先检查当前环境是否支持对应 CLI,并向我确认将要执行的命令、安装目录、联网范围和文件读写权限;确认后再执行。

命令行安装

复制命令到本机终端执行。该命令会通过 OpenClaw 从第三方来源获取 Skill;本站只展示命令,不托管安装包,也不自动执行。

ClawHubOpenClaw
openclaw skills install bookforge-build-refactoring-test-suite

简介

用于在重构前构建足够的自动化测试套件,保障变更安全并提供回归验证能力。

  • 适用于 OpenClaw 环境中的测试设计与回归验证场景。
  • 应用六步顺序流程:测试类→夹具→正常等确保覆盖关键模块。
  • 使用时需确认现有代码结构和测试框架,避免误改真实逻辑。
  • 涉及浏览器或外部服务时应区分本地模拟与生产环境,优先覆盖高风险模块。

SKILL.md

name
build-refactoring-test-suite
description
Build a sufficient automated test suite before refactoring existing code by applying a 6-step sequential construction workflow (test class → fixture → normal behavior → boundary conditions → expected errors → green-suite gate) and a bug-fix variant (write failing test first → reproduce → fix → verify green). Use this skill when you are about to refactor a class or module that lacks tests, when a bug report arrives and you need to pin it down before fixing it, when you want to establish the compile-and-test gate that makes every subsequent refactoring step safe to revert, or when you need to assess whether an existing test suite is adequate to protect a planned refactoring.
version
1.0.0
homepage
https://github.com/bookforge-ai/bookforge-skills/tree/main/books/refactoring/skills/build-refactoring-test-suite
metadata
{"openclaw":{"emoji":"🧪","homepage":"https://github.com/bookforge-ai/bookforge-skills"}}
status
draft
source-books
title
Refactoring: Improving the Design of Existing Code
authors
["Martin Fowler"]
chapters
[4]
tags
[refactoring, testing, code-quality]
depends-on
[]
execution
tier
2
mode
hybrid
inputs
description
A class, module, or file to be refactored, with or without existing tests.
tools-required
[Read, Write, Bash]
tools-optional
[]
mcps-required
[]
environment
Working codebase with a test runner available. Output: a runnable test file that passes green, covering normal behavior, boundary conditions, and expected errors for the code under refactoring.
discovery
goal
Produce a fast, self-checking test suite that turns green before any refactoring step begins and re-runs in seconds after every atomic change.
tasks
audience
developers, engineers, anyone refactoring existing production code without adequate test coverage
when_to_use
When a class or module needs to be refactored and does not yet have a test suite sufficient to detect regressions after each atomic change
environment
Existing codebase. The code under refactoring should be readable. A test runner (any language) must be available.
quality
placeholder

Build Refactoring Test Suite

When to Use

You are about to refactor code — extracting methods, moving fields, changing conditionals — and one of these is true:

  • The code has no tests at all
  • The existing tests are incomplete, untargeted, or only cover happy paths
  • A bug has been reported and you need to pin it down before fixing and refactoring around it
  • You are inheriting code from someone else and want a safety net before touching anything

This is the Level 0 foundation skill: every other refactoring mechanic in Fowler's catalog assumes this suite exists. Without it, you are refactoring blind. With it, every subsequent step is reversible — if a test turns red, you revert and try smaller steps.

The core pattern: build a self-checking test suite that runs in seconds, covers the code you are about to change, and can answer one question without human inspection: "Did I break anything?"

Before starting, confirm you have:

  • Read access to the class or module being refactored
  • A test runner installed for the language (pytest, JUnit, RSpec, Vitest, go test, etc.)
  • The ability to run the test suite from the command line

Context and Input Gathering

Required Context

  • Target code: The class, module, or file to be refactored. Read it fully before writing any tests.
  • Language and test framework: Identify from the project structure (e.g., pyproject.toml, package.json, pom.xml, go.mod). Use the framework already in place — do not introduce a new one.
  • Existing tests: Check for any test files that already cover the target. Run them first. If they all pass, extend rather than replace.

Observable Context

Scan the target code for:

  • Public interface: Every public method, function, or exported symbol is a testing target. Private internals are not — test through the public interface only.
  • Inputs and outputs: What does each method take in and return? These define what to assert.
  • Error conditions: What inputs should raise exceptions, return error codes, or produce empty results? These drive the error-path tests.
  • State mutations: Does the class modify shared state? Fixtures must initialize and tear down that state per-test.
  • External dependencies: Databases, files, network calls. These need either real test fixtures or test doubles (mocks/stubs). Prefer real fixtures for refactoring — mocks can hide regressions.

Default Assumptions

  • If no test framework exists → pick the language's idiomatic standard (pytest for Python, JUnit for Java, Vitest for TypeScript, etc.)
  • If the code reads external files → create small, dedicated test data files in a testdata/ or fixtures/ directory
  • If the code has database calls → prefer an in-memory or test-mode database over mocking; mocks test the mock, not the code
  • If tests already exist and pass → run them first, then add missing coverage; do not re-implement passing tests

Sufficiency Check

You are ready to start when:

  1. You can read the target class/module completely
  2. You know which test framework is in use
  3. You know at least three things the code is supposed to do (its public contract)

If you cannot determine what the code is supposed to do (no comments, no documentation, unclear naming), read the calling code or integration tests first to reconstruct the intended behavior before writing unit tests.


Process

Step 1 — Create the Test Class/File

Create a dedicated test file for the code under refactoring. Place it where the project's test convention dictates (e.g., tests/test_order.py, src/__tests__/Order.test.ts, OrderTest.java).

Why: Each class under test needs its own test container. Mixing multiple classes into one test file makes isolation harder and failure messages harder to read. Using the project's existing naming convention ensures the test runner discovers the file automatically.

Minimal structure:

# Python
class TestOrder:
    pass

# Java
class OrderTest extends TestCase { }

# TypeScript
describe('Order', () => { })

# Go
func TestOrder(t *testing.T) { }

Run the empty test file immediately to confirm the runner finds and executes it without errors.


Step 2 — Implement Setup and Teardown Fixtures

Before writing any test methods, define the shared state that every test will need. The test framework's setUp/beforeEach/setup hook runs before each test; tearDown/afterEach/cleanup runs after.

Why: Each test must be fully isolated — it must not depend on execution order, and it must not leave side effects that corrupt the next test. Setup creates a fresh environment; teardown cleans up resources (open files, database connections, temp files). Without this isolation, a failure in test 3 can cause test 4 to fail for unrelated reasons, making debugging misleading.

Guidelines:

  • Initialize only what is shared across most tests in the fixture. Test-specific state belongs in the test method itself.
  • If setup can fail (file not found, connection refused), let the error propagate — a setup failure is a hard stop, not a test failure.
  • If teardown involves resource release (closing files, dropping test tables), do it unconditionally — use finally blocks or the framework's guaranteed cleanup mechanism.
# Python example
class TestFileProcessor:
    def setup_method(self):
        self.input_file = open("testdata/sample.txt", "r")

    def teardown_method(self):
        self.input_file.close()

Step 3 — Write Tests for Normal / Expected Behavior

For each public method, test the central, intended behavior first — the happy path. Ask: "What is this method supposed to do when given valid, typical input?"

Why: Start with normal behavior so you confirm the code works correctly before probing its edges. If normal behavior tests fail, the code is broken before you even touch it — that is useful information and must be resolved before any refactoring begins.

Rules:

  • One behavior per test method. Do not write omnibus tests that check five things in sequence — when one assertion fails, you cannot tell which behavior broke.
  • Name tests descriptively: test_read_returns_correct_character, not test1. Descriptive names are the failure message.
  • Assert the specific output, not just "no exception was raised." Confirm the actual value.
  • Write the test, then verify it can fail: temporarily corrupt the assertion value (e.g., assert 'x' == result instead of 'd' == result). If it does not fail, the test is not testing what you think.
def test_read_returns_correct_character(self):
    # advance past the first three characters
    for _ in range(3):
        self.input_file.read(1)
    ch = self.input_file.read(1)
    assert ch == 'd'  # fourth character in the test file

Step 4 — Add Boundary Condition Tests

After normal behavior is covered, identify the boundaries where behavior could change or break. Boundary conditions are the most productive place to find bugs.

Why: Most bugs hide at the edges — the first item, the last item, the empty collection, the zero value, the maximum value. Fowler calls this "playing the part of an enemy to your own code" — actively trying to find the conditions under which the code will fail, rather than confirming it works for typical input.

Common boundary categories:

CategoryExamples
Sequence edgesFirst element, last element, element after the last
Empty inputsEmpty string, empty list, empty file, zero-length collection
Zero / null valuesZero quantity, null reference, None, empty optional
Maximum / minimum valuesInteger overflow boundary, max string length, single-item list
Repeated callsReading past end-of-file twice, calling close twice

For each boundary, write a separate test method. Add a descriptive message to assertions so that when a boundary test fails, the output tells you which boundary broke.

def test_read_at_end_of_file_returns_minus_one(self):
    # consume all 141 characters
    for _ in range(141):
        self.input_file.read(1)
    result = self.input_file.read(1)
    assert result == -1, "read at end of file should return -1"

def test_read_from_empty_file_returns_minus_one(self):
    empty = open("testdata/empty.txt", "r")
    result = empty.read(1)
    empty.close()
    assert result == -1, "read from empty file should return -1"

Step 5 — Write Tests for Expected Errors and Exceptions

Test that error conditions produce the correct error, not just that they do not crash silently. If the code's contract says "raises ValueError on negative input" or "raises IOError if the stream is closed," write a test that verifies exactly that.

Why: Errors are part of the public contract. Failing to raise the expected error — or raising the wrong one — is a bug. These tests also protect against future refactoring silently swallowing exceptions.

Pattern:

  • Close the resource intentionally, then attempt an operation — expect the specific error.
  • Use the framework's pytest.raises, assertRaises, or expect { }.to raise_error idiom.
  • If the test body completes without the expected error, force an explicit failure: fail("expected error was not raised").
def test_read_after_close_raises_io_error(self):
    self.input_file.close()
    with pytest.raises(IOError):
        self.input_file.read(1)
    # if no IOError is raised, pytest.raises will fail the test automatically

Step 6 — Run the Full Suite: Green Gate

Run the entire test suite. All tests must pass — green — before any refactoring step begins.

Why: This is the precondition that makes refactoring safe. If the suite is red before you start, you do not know whether a subsequent red result was caused by your change or by a pre-existing bug. You must start from a known-good baseline.

What to do if tests are red before you start:

  1. Do not begin refactoring yet.
  2. Determine whether the failure is a test bug (wrong assertion) or a production bug.
  3. If it is a production bug, decide: fix it first, or document it as a known failure and exclude that test from the baseline. Do not silently ignore red tests.
  4. Once all tests pass (or excluded failures are documented), the green gate is established.

The compile-and-test gate (applies to every subsequent step): Once the suite is green and refactoring begins, apply this gate after every single atomic change — not after a batch of changes:

make one atomic change → compile/lint → run test suite
  green → continue to next change
  red   → revert immediately, try a smaller step

"Atomic" means the smallest possible change that can be independently compiled and tested: extract one method, rename one variable, move one field. Never accumulate multiple changes before testing. Small steps mean small reverting cost.

If a language has a compiler, compile first — compilation errors caught before test execution are faster feedback than test failures.


Bug-Fix Variant: Test-First Bug Reproduction

When fixing a bug rather than refactoring, use this variant workflow:

  1. Write a failing test that reproduces the bug. Do this before touching production code. The test should fail because the bug exists.
  2. Confirm the test fails. Run it. If it passes, the test is wrong — it is not actually testing the buggy behavior.
  3. Fix the production code to make the test pass.
  4. Run the full suite. All tests should be green. If new failures appeared, your fix introduced a regression.

Why test first for bugs: Writing the test first forces you to understand exactly what the bug is, not approximately what it is. It also prevents you from accidentally fixing a different problem and convincing yourself the bug is gone. And the test permanently guards against the same bug recurring.

When a bug report arrives:

  • Start by writing a unit test that exposes the bug, not by opening the source file.
  • If you need multiple tests to narrow the scope of the bug (to rule out related failures), write all of them before fixing anything.
  • The unit tests become the regression suite for this bug forever.

Test Adequacy Criteria

A test suite is sufficient for refactoring when it satisfies all four of these criteria:

CriterionWhat to Check
Normal behavior coveredEvery public method has at least one test for its primary intended behavior
Boundaries coveredEach method has tests for: empty input, first/last element, value after the last, zero/null values
Error paths coveredEvery documented error condition or exception has a test that verifies it is raised correctly
Fast enough to run after every stepThe full suite completes in under 30 seconds. If it takes longer, it will not be run frequently enough

What you do not need:

  • 100% branch coverage — Fowler explicitly rejects coverage targets as the goal. The goal is testing where the risk is.
  • Tests for simple accessors (getters/setters that do nothing but read/write a field) — too simple to fail.
  • Tests for every combination in a class hierarchy — test each alternative independently; only test combinations where the alternatives interact in complex ways.

Fowler's practical rule: Test the areas you are most worried about going wrong. Concentrate effort where complexity is highest and where bugs would be hardest to find manually. It is better to run incomplete tests than to have no tests because a complete suite felt impossible to write.


Key Principles

1. Tests must be self-checking. Tests that print output to the console for a human to inspect are not self-checking. Every assertion must be evaluated by the framework automatically. The only acceptable output is a pass/fail signal — ideally a progress bar that turns red on failure.

2. Tests must be fast. Slow tests do not get run. If the suite takes more than 30 seconds, developers will batch changes and run tests infrequently. Infrequent testing means bugs accumulate between runs, making them harder to isolate. For refactoring specifically, tests must be fast enough to run after every single atomic step.

3. Each test must be isolated. A test must not depend on the results of any other test. Execution order must not matter. Use setup/teardown to ensure each test starts from an identical, known state.

4. Verify that tests can fail. When you write a test, temporarily insert a wrong value into the assertion. If the test does not turn red, it is not exercising what you think. A test that cannot fail is not a test — it is false confidence.

5. Incomplete tests beat no tests. The most common failure mode is paralysis: "I can't test everything perfectly, so I won't test anything." Write the tests for the risky areas first. Run them. An imperfect suite that runs frequently is vastly more valuable than a theoretically complete suite that never gets written.

6. The compile-and-test gate is non-negotiable. Every atomic refactoring step ends with: compile + run suite. Red = revert. No exceptions. This is what makes refactoring safe to do in a production codebase.


Examples

Example 1: Adding tests before extracting methods from a billing class

Situation: You want to decompose a 200-line calculate_invoice() method into smaller methods but there are no tests.

Setup fixture: Create an Invoice object with known line items and tax rates. Normal behavior tests: Assert that calculate_invoice() returns the correct total for a standard order. Boundary tests: Empty order (zero line items), single item, order with a discount applied to zero-priced items. Error tests: Negative quantity raises ValueError, unknown product code raises KeyError. Green gate: All pass. Now decompose calculate_invoice() one extracted method at a time, running after each extraction.


Example 2: Bug-fix variant for a reported pricing error

Situation: A bug report says orders over $1,000 are applying the discount twice.

Step 1 — Write failing test:

def test_discount_applied_once_for_large_order(self):
    order = Order(items=[Item("product-A", quantity=10, unit_price=150)])  # total = $1,500
    assert order.total_price() == 1350.00  # 10% discount applied once

Step 2 — Run it. It fails (returns 1215.00 — discount applied twice). Good. The test reproduces the bug. Step 3 — Fix the discount logic. Step 4 — Run full suite. All green including the new test. Bug is fixed and regression-protected.


Example 3: Assessing an existing test suite before a large refactoring

Situation: A module has 12 tests. You want to refactor its data model.

Audit checklist:

  • [ ] Does every public method have at least one test? — Check: 3 public methods, 12 tests → appears covered
  • [ ] Are boundaries tested? — Check: no test for empty input, no test for maximum collection size → gap found
  • [ ] Are error paths tested? — Check: no test for invalid state transition → gap found
  • [ ] Does the suite run in under 30 seconds? — Check: 4.2 seconds → acceptable

Action: Add boundary and error path tests. Run. Green. Now proceed with the refactoring.


License

This skill is licensed under CC-BY-SA-4.0. Source: BookForge — Refactoring: Improving the Design of Existing Code by Martin Fowler.

Related BookForge Skills

  • refactoring-readiness-assessment — Assess whether code is ready to refactor
  • code-smell-diagnosis — Identify which smells to address first
  • method-decomposition-refactoring — Apply once this test suite is green

Browse more BookForge skills: bookforge-skills

适合场景

01

OpenClaw 用户查找和安装 Skill 时

02

用户想查找某类 Agent Skill 时

03

需要根据任务场景推荐可安装能力包时

04

需要对比不同来源的安装命令和来源信息时

能力概览

能力 1

按任务关键词查找相关 Skills

能力 2

展示可复制的安装命令

能力 3

保留来源站点、仓库和原始说明,方便继续核验

能力 4

补充不同宿主或平台的使用分布数据

能力 5

展示第三方安全扫描或审计结果

安装后应在对应宿主中按原始 README 的触发条件使用;具体调用方式请以来源页面和 README 为准。

平台分布

OpenClaw

94.1%
按下载量换算1,247

安全审计

VirusTotal

通过

ClawScan

通过

Static analysis

通过

权限和风险

external-service

该 Skill 可能调用第三方服务、云服务或外部模型 API,使用前需要确认账号、额度、数据发送范围和服务条款。

安装前确认

本站仅展示第三方公开信息,不托管安装包,不提供自动安装或运行环境。安装前应自行审查源码、依赖和命令行为。当前只有一个来源,正式发布前建议补源仓库或其他目录站核验。

来源信息

继续浏览同类 Skills