跪拜 Guibai
← Back to the summary

A Frontend Unit-Test Skill That Cut AI Token Costs by 73%

Hi~, I'm Sanjin.

When I first started producing AI unit tests for the team, the concept of Skills didn't exist yet, so I just created a Command and used prompts to generate unit tests.

In the early stages of use, it felt like output efficiency had skyrocketed, and it could self-check and self-correct. But later, I discovered some major pitfalls:

After Skills came out, I planned to migrate the unit test Command to a Skill. To effectively solve the above problems, the initial approach was:

Regarding rule constraints, another senior member of the team also shared some thoughts on this area: abstract several different types of modules from the architectural level, and then customize a unit test template for each module. Thus, the first version of the unit test Skill was born, with the following directory structure:

Regarding the process, you can also use it as a reference:

The specific content of the Skill cannot be shared, but roughly it boils down to the following points (feel free to add more in the comments if I missed any):

  1. Happy Path: Use typical, valid inputs (props, initial state, context) to verify that the component renders the main UI content correctly and that the main business processes (like form submission, data loading) can execute smoothly and produce expected results. The focus of these cases is to verify that core functionality is available without crashes or errors.
  2. Boundary Values: Test against critical points in data ranges, such as empty arrays, single-element arrays, maximum/minimum values, the first/last pages of pagination, string length limits, etc. These cases mainly aim to solve some extreme problems or easily overlooked issues—things we think won't happen but indeed might. The focus is on the behavior of loops, list rendering, pagination controls, numerical calculations, etc., under extreme inputs, avoiding issues like array out-of-bounds, division by zero, or infinite loops.
  3. Negative Scenarios: Simulate error conditions, such as API request failures, components receiving invalid props, child components throwing errors, user operations triggering exceptions, etc. The focus of these cases is whether the component can degrade gracefully (display error feedback, fallback UI), correctly catch or throw errors, and whether error boundaries work as expected.
  4. Null / Empty / Undefined: Target inputs that may be null, undefined, empty strings, empty objects, or empty collections, ensuring the component doesn't crash and can display reasonable placeholder content or default states. This is a very common and error-prone case, so the focus is on the safety when optional props are missing and whether defensive code (like optional chaining ?., default values) operates correctly.
  5. Branch Coverage: Ensure that every branch of all conditional statements (if/else, switch/case, ternary operators, logical && short-circuiting) within the component is executed at least once. This type of problem is also very common, sometimes even involving very low-level errors, so the focus is on the correctness of rendering differences under different roles/permissions, login/logout state switching, feature flags, and other branching logic.
  6. State & Interaction: Simulate real user operations (click, input, scroll, focus/blur, etc.) to verify the update of the component's internal state or external store, and whether callback functions passed to child components are called correctly (number of calls, passed parameters). I actually planned to do this directly in E2E tests, but ultimately added it in. In front-end components, this is an unavoidable unit test. The focus is on event handler side effects (like initiating requests, updating caches), responses to useEffect dependency changes, cleanup function execution, and the logical correctness of custom Hooks.

After being put into use for a period, multiple problems were exposed, two of which were quite serious: time consumption + token cost.

For this reason, I iterated several more versions. The optimized version:

For AI project engineering, the methodologies are largely similar, with differences only existing in the details, such as those caused by different tech stacks, architectures, or business contexts. We can adapt to local conditions, make some "localization" changes on top of mature tools, and quickly build an AI system suitable for business needs.