CtrlK
BlogDocsLog inGet started
Tessl Logo

phpunit-test-naming

Enforces grammatically correct, intention-revealing PHPUnit test method names

64

Quality

77%

Does it follow best practices?

Run evals on this skill

Adds up to 20 points to the overall score

View guide

SecuritybySnyk

Passed

No findings from the security scan

Fix and improve this skill with Tessl

tessl review fix ./.claude/skills/phpunit-test-naming/SKILL.md
SKILL.md
Quality
Evals
Security

PHPUnit Test Naming Convention

The Rule

Every test method MUST start with it_ and form a grammatically correct English sentence when you replace underscores with spaces.

it_{verb}_{object}_{optional_qualifiers}

"it" is the grammatical subject — "it" = the feature/system under test.

"It returns 404 for a nonexistent record."
"It stores the entity and fires the expected event."
"It rejects the action when a precondition is not met."

✅ Correct

public function it_returns_200_when_record_is_found(): void {}
public function it_redirects_to_index_when_user_lacks_permission(): void {}
public function it_stores_entity_with_valid_payload(): void {}
public function it_rejects_invalid_status_for_task(): void {}
public function it_soft_deletes_parent_and_preserves_children(): void {}
public function it_sends_403_json_when_access_is_denied(): void {}
public function it_creates_line_item_and_attaches_it_to_parent(): void {}
public function it_calculates_total_price_excluding_tax(): void {}
public function it_authorized_user_can_delete_task(): void {} // ✅ reads: "it [is that an] authorized user can delete task"
public function it_unauthorized_user_cannot_delete_task(): void {} // ✅ same pattern

❌ Wrong — and why

Bad nameProblemFix
it_user_can_view_document"it user" is not Englishit_authorized_user_can_view_document
a_user_can_view_documentDoesn't start with it_it_authorized_user_can_view_document
test_duplicates_are_removedtest_ prefix — use #[Test] attribute insteadit_removes_duplicate_statuses_in_response
test_it_can_access_create_taskDouble prefix: test_it_it_can_access_task_create_page
it_can_list_clients_indexRedundant "index" after verb "list"it_lists_all_clients_on_index_page

The it_user_* smell

it_user_can_* was historically used for user-permission tests. The fix is NOT to rename to a_user_* — that just swaps one broken convention for another. The correct patterns are:

// For permission tests: keep "user" as a qualifier on the subject
it_authorized_user_can_delete_task()
it_unauthorized_user_cannot_delete_task()

// Or: drop the subject, make it about the outcome
it_deletes_task_when_user_has_delete_permission()
it_returns_403_when_user_lacks_delete_permission()

Attribute, Not Prefix

Never use test_ prefix. Always annotate with #[Test]:

// ❌
public function test_creates_record() {}

// ✅
#[Test]
public function it_creates_record(): void {}

Return Type

All test methods must declare : void.

Checklist When Writing a Test Name

  1. Does it start with it_?
  2. Read it aloud replacing underscores with spaces. Does it sound like a sentence?
  3. Is the verb meaningful? (returns, stores, rejects, sends, deletes, redirects, calculates, creates, lists, shows)
  4. Does the name reveal the scenario, not just the action? (it_rejects_payment_when_invoice_is_not_sent > it_cannot_add_payment)
  5. Return type : void declared?

Audit Command

# Find non-conforming names (test_, a_user_, it_user_, no it_ prefix)
grep -rn "public function " tests/ --include="*.php" \
  | grep -vE "setUp|tearDown|__construct|protected |abstract " \
  | grep -vE "public function it_" \
  | grep -v "Browser/"
Repository
Bottelet/DaybydayCRM
Last updated
First committed

Is this your skill?

If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.