本文へ移動
cccskills
無料GitHub で公開

typescript-tests

Write TypeScript tests. A test name is a sentence, the body is setup, mock, test and verify blocks separated by blank lines with no comments, and the runner's config decides where the file goes. Covers naming, the block shape, size, fixtures, what not to assert, DOM opt-in, multiple runner projects, coverage, and running one test. Use when adding or changing any .test.ts or .test.tsx, or when coverage has to come up. Triggers - "write a test", "add tests", "test this", "vitest", "jest", "how do I test", "cover this file".

日本語の概要は準備中です。原文の説明を表示しています。

インストール方法を見る

含まれるファイル(1)

  • SKILL.md7.3 KB

SKILL.md(原文)

インストールする前に、エージェントに与えられる指示の中身を確認できます。

TypeScript tests

A test earns its place by being read, not by existing. Three rules carry most of it: the name is the sentence, the body is blocks separated by blank lines, and there are no comments.

it('falls back to the handed shape when the stage measures zero', () => {
  const node = stage('stage-b', 0, 0);

  vi.spyOn(node, 'getBoundingClientRect').mockReturnValue({ width: 0, height: 0 } as DOMRect);

  const { result } = renderHook(() => useStageRatio('stage-b', 4 / 3));

  expect(result.current).toBeCloseTo(4 / 3, 6);
});

The name says what is guaranteed and the blank lines say where each phase ends, so nothing is left for a comment to say.

Examples here use vitest. Jest, node:test and bun:test take the same shape; read two neighbouring test files for the project's runner, import style and helpers before writing a third.

Naming

The name completes "it ...", describes the behaviour, and never mentions the implementation.

it('rejects a range past EOF', ...)                 // good
it('marks a file absent rather than deleting it', ...)  // good

it('should work', ...)                              // says nothing
it('test parseRange', ...)                          // names the function, not the behaviour
it('returns false when the second argument is undefined', ...)  // describes the code

If you cannot name it in a sentence, the test is doing two things. Split it.

The four blocks

A body is up to four blocks, always in this order, one blank line between them:

BlockHolds
setupFixtures, state, the world the behaviour needs
mockStubs, spies, fakes, and what they are told to return
testThe one action under test
verifyThe assertions

Rules that keep the shape readable:

  • A blank line only ever separates two blocks. Never one inside a block, and never two in a row. A stray blank line reads as a phase boundary that is not there.
  • An empty block is absent, not blank. A test with nothing to mock has three blocks. A one-line setup and a one-line assertion is two.
  • No comment names a block. // Arrange, // Act, // Assert, // setup and // given are all the blank line written twice. The order is the label.
  • One action in the test block. Two calls under test means two tests, unless the second is the only way to observe the first.
  • Every assertion in verify. An expect sitting in the setup block is a precondition check, and a precondition worth checking is its own test.
it('marks a file absent rather than deleting its history', async () => {
  const library = await seedLibrary({ files: ['a.mkv'] });
  await library.watch('a.mkv', { seconds: 640 });

  vi.spyOn(fs, 'stat').mockRejectedValue(new Error('ENOENT'));

  await rescan(library);

  expect(library.get('a.mkv')).toMatchObject({ absent: true });
  expect(library.progress('a.mkv')).toEqual({ seconds: 640 });
});

Two assertions, because "marks absent" and "history survives" are one behaviour seen from two sides. Three unrelated assertions would be three tests.

Size

A test that runs past roughly fifteen lines is usually one of three things:

  • Two tests sharing a body. Split them.
  • A fixture that wants extracting into a named helper above the describe.
  • Production code that needs three collaborators to say anything, which is a design signal, not a testing problem.

Keep the helper small and name it after what it builds:

function stage(id: string, width: number, height: number): HTMLElement {
  const node = document.createElement('div');
  node.id = id;
  node.getBoundingClientRect = () => ({ width, height }) as DOMRect;
  document.body.append(node);
  NODES.push(node);
  return node;
}

No comments

The rule holds hardest here, because a test comment is nearly always the name saying it should have been rewritten, or a block label the blank line already gave. No file header explaining what the suite pins, no note above a case, no rationale paragraph.

The one comment that survives is a genuinely non-obvious fixture value: why this byte, why this timestamp. One line, stating the reason, not the value.

Assert behaviour, not shape

  • Assert what a caller would notice. A snapshot of an internal structure fails on every refactor and catches nothing.
  • Never assert a mock was called when you can assert what the call produced.
  • A test that still passes with the implementation deleted tests nothing. Check by deleting it, briefly, when you are unsure.
  • Do not weaken an assertion to make a suite green. Either the behaviour changed, and the name changes with it, or there is a bug.

Where the file goes

Beside the code it covers, named after it. The runner's config owns the rest, so read it rather than guessing: it holds the include globs, the default environment, and whether the project splits its suites into more than one project.

Two things to check in that config before naming a file:

  • The environment. Many projects default to node and make a DOM opt-in, per file, on the first line:

    // @vitest-environment jsdom
    
  • Multiple projects. A repo that ships to more than one platform often runs the same suites twice under different module resolution, with a suffix deciding which project owns a file. Where that is true, the suffix is the only thing making a test run at all, and a file named for the wrong project silently never executes.

Where the file goes on disk, and what its suffix means, belong to the naming skill.

Running one

Read the manifest scripts for the project's wrapper, then narrow:

<test script> path/to/one.test.ts
<test script> -t 'rejects a range'
<test script> --project <name> -t 'rejects a range'

Run the narrow form while writing and the full suite before handing the work back. A test that passes alone and fails in the suite is sharing state.

Coverage

Write the test where it reaches the logic. If the logic sits somewhere no test can call, move the logic rather than excluding the file: new logic goes where a test can reach it.

Two traps worth knowing before trusting a percentage:

  • A coverage tool that only instruments what the tests loaded reads far higher than a scanner that counts every file in scope. A file no test imports is absent from the first number and a zero in the second. Check which one the project's gate uses.
  • Some branches are unreachable under the runner. A require() fallback or a build-time environment branch cannot be taken, so a test that appears to cover it passes without asserting anything.

Check the runner's coverage config for excluded paths before writing a test that cannot count. Where a file type is excluded, behaviour worth pinning belongs in one that is not.

レビュー

まだレビューはありません。使ってみた感想をお寄せください。

コメント

ログインなしで書き込めます。使ってみた感想、質問、動かなかった環境などを気軽にどうぞ。

まだコメントはありません。最初のコメントを書いてみませんか。

送信するとすぐに公開されます。メールアドレスや電話番号などの個人情報は書かないでください。URL は 2 つまでです。

0 / 1000 文字

このフォームは reCAPTCHA で保護されており、Google のプライバシーポリシーと利用規約が適用されます。

同じリポジトリのスキル

One file, one job, and short enough to hold in your head. Covers the size policy (hard-split over 300 lines, split 200 to 300 at a seam, aim for 150), what a natural seam is and where to cut, the exemptions, the dependency rules that decide where the split lands, and the signals that a file is already two files. Use before adding to a file that is already long, when naming a new file, when a function has nowhere obvious to live, or when a review says a file is doing too much. Triggers - "this file is too long", "where should this go", "split this file", "is this file too big", "one responsibility".

日本語の概要は準備中です。原文の説明を表示しています。

kromatv/kroma602026年10月19日 更新

naming

無料

What things get called. A file is named after its export in the project's case convention, a suffix is a resolution instruction rather than decoration, an index file only re-exports, and some symbol names are always wrong (utils, helpers, V2, Enhanced, New). Covers learning a tree's local vocabulary before adding to it, test and platform-variant naming, and the per-language conventions. Use when creating a file or directory, naming a test file, adding a platform variant, or when a name has to change. Triggers - "what should I call this file", "where does this file go", "naming convention", "kebab or camel", "name this test".

日本語の概要は準備中です。原文の説明を表示しています。

kromatv/kroma602026年10月19日 更新

Spawn the comment hunter, fix accepted findings, and offer encodings for claimed constraints.

日本語の概要は準備中です。原文の説明を表示しています。

kromatv/kroma602026年10月19日 更新

Write a short release note people finish reading. Picks the few changes a user will notice from the pull requests since the last release, names each by what the reader can now do, keeps to a length budget, writes every language from the intent instead of translating, and traces every line to a PR. Use when cutting a release, or filling a GitHub Release body, a store "what's new", a package changelog or an in-app what's new screen. Triggers - "write the release notes", "release notes for 0.4.0", "what's new", "changelog for this release", "notes de version".

日本語の概要は準備中です。原文の説明を表示しています。

kromatv/kroma602026年10月19日 更新

Rust best practices. Use when reading or editing any .rs file, designing a crate's public API, choosing an error type, splitting a workspace, or writing async code on tokio. Triggers - "rust best practices", "is this idiomatic", "should this be a newtype", "anyhow or thiserror", "why does the borrow checker", "review this Rust".

日本語の概要は準備中です。原文の説明を表示しています。

kromatv/kroma602026年10月19日 更新

Write cargo tests. A test name is a sentence, the body is setup, mock, test and verify blocks separated by blank lines with no comments, and shared setup lives in a named test-support module rather than in each test. Covers naming, the block shape, where a test file goes, async and time, workspaces, and running one crate or one filter. Use when adding or changing any #[test] or #[tokio::test], or when Rust coverage has to come up. Triggers - "write a rust test", "cargo test", "test this crate", "cover this handler".

日本語の概要は準備中です。原文の説明を表示しています。

kromatv/kroma602026年10月19日 更新

kromatv のスキルをすべて見る

このスキルの問題を報告する