Stay Updated!

Get the latest posts and insights delivered directly to your inbox

Skip to content

Frontend Testing Guide: 10 Essential Rules for Naming Tests

Published:Β atΒ 

Introduction#

The path to better testing starts with something surprisingly simple: how you name your tests. Good test names:

In this blog post, we’ll explore 10 essential rules for writing better tests that will transform your approach to testing. These principles are:

  1. Framework-agnostic
  2. Applicable across the entire testing pyramid
  3. Useful for various testing tools:
    • Unit tests (Jest, Vitest)
    • Integration tests
    • End-to-end tests (Cypress, Playwright)

By following these rules, you’ll create a more robust and understandable test suite, regardless of your chosen testing framework or methodology.

Rule 1: Always Use β€œshould” + Verb#

Every test name should start with β€œshould” followed by an action verb.

// ❌ Bad
it("displays the error message", () => {});
it("modal visibility", () => {});
it("form validation working", () => {});

// βœ… Good
it("should display error message when validation fails", () => {});
it("should show modal when trigger button is clicked", () => {});
it("should validate form when user submits", () => {});

Generic Pattern: should [verb] [expected outcome]

Rule 2: Include the Trigger Event#

Specify what causes the behavior you’re testing.

// ❌ Bad
it("should update counter", () => {});
it("should validate email", () => {});
it("should show dropdown", () => {});

// βœ… Good
it("should increment counter when plus button is clicked", () => {});
it("should show error when email format is invalid", () => {});
it("should open dropdown when toggle is clicked", () => {});

Generic Pattern: should [verb] [expected outcome] when [trigger event]

Use describe blocks to create clear test hierarchies.

// ❌ Bad
describe("AuthForm", () => {
  it("should test empty state", () => {});
  it("should test invalid state", () => {});
  it("should test success state", () => {});
});

// βœ… Good
describe("AuthForm", () => {
  describe("when form is empty", () => {
    it("should disable submit button", () => {});
    it("should not show any validation errors", () => {});
  });

  describe("when submitting invalid data", () => {
    it("should show validation errors", () => {});
    it("should keep submit button disabled", () => {});
  });
});

Generic Pattern:

describe("[Component/Feature]", () => {
  describe("when [specific condition]", () => {
    it("should [expected behavior]", () => {});
  });
});

Rule 4: Name State Changes Explicitly#

Clearly describe the before and after states in your test names.

// ❌ Bad
it("should change status", () => {});
it("should update todo", () => {});
it("should modify permissions", () => {});

// βœ… Good
it("should change status from pending to approved", () => {});
it("should mark todo as completed when checkbox clicked", () => {});
it("should upgrade user from basic to premium", () => {});

Generic Pattern: should change [attribute] from [initial state] to [final state]

Rule 5: Describe Async Behavior Clearly#

Include loading and result states for asynchronous operations.

// ❌ Bad
it("should load data", () => {});
it("should handle API call", () => {});
it("should fetch user", () => {});

// βœ… Good
it("should show skeleton while loading data", () => {});
it("should display error message when API call fails", () => {});
it("should render profile after user data loads", () => {});

Generic Pattern: should [verb] [expected outcome] [during/after] [async operation]

Rule 6: Name Error Cases Specifically#

Be explicit about the type of error and what causes it.

// ❌ Bad
it("should show error", () => {});
it("should handle invalid input", () => {});
it("should validate form", () => {});

// βœ… Good
it('should show "Invalid Card" when card number is wrong', () => {});
it('should display "Required" when password is empty', () => {});
it("should show network error when API is unreachable", () => {});

Generic Pattern: should show [specific error message] when [error condition]

Rule 7: Use Business Language, Not Technical Terms#

Write tests using domain language rather than implementation details.

// ❌ Bad
it("should update state", () => {});
it("should dispatch action", () => {});
it("should modify DOM", () => {});

// βœ… Good
it("should save customer order", () => {});
it("should update cart total", () => {});
it("should mark order as delivered", () => {});

Generic Pattern: should [business action] [business entity]

Rule 8: Include Important Preconditions#

Specify conditions that affect the behavior being tested.

// ❌ Bad
it("should enable button", () => {});
it("should show message", () => {});
it("should apply discount", () => {});

// βœ… Good
it("should enable checkout when cart has items", () => {});
it("should show free shipping when total exceeds $100", () => {});
it("should apply discount when user is premium member", () => {});

Generic Pattern: should [expected behavior] when [precondition]

Rule 9: Name UI Feedback Tests from User Perspective#

Describe visual changes as users would perceive them.

// ❌ Bad
it("should set error class", () => {});
it("should toggle visibility", () => {});
it("should update styles", () => {});

// βœ… Good
it("should highlight search box in red when empty", () => {});
it("should show green checkmark when password is strong", () => {});
it("should disable submit button while processing", () => {});

Generic Pattern: should [visual change] when [user action/condition]

Rule 10: Structure Complex Workflows Step by Step#

Break down complex processes into clear steps.

// ❌ Bad
describe("Checkout", () => {
  it("should process checkout", () => {});
  it("should handle shipping", () => {});
  it("should complete order", () => {});
});

// βœ… Good
describe("Checkout Process", () => {
  it("should first validate items are in stock", () => {});
  it("should then collect shipping address", () => {});
  it("should finally process payment", () => {});

  describe("after successful payment", () => {
    it("should display order confirmation", () => {});
    it("should send confirmation email", () => {});
  });
});

Generic Pattern:

describe("[Complex Process]", () => {
  it("should first [initial step]", () => {});
  it("should then [next step]", () => {});
  it("should finally [final step]", () => {});

  describe("after [key milestone]", () => {
    it("should [follow-up action]", () => {});
  });
});

Complete Example#

Here’s a comprehensive example showing how to combine all these rules:

// ❌ Bad
describe("ShoppingCart", () => {
  it("test adding item", () => {});
  it("check total", () => {});
  it("handle checkout", () => {});
});

// βœ… Good
describe("ShoppingCart", () => {
  describe("when adding items", () => {
    it("should add item to cart when add button is clicked", () => {});
    it("should update total price immediately", () => {});
    it("should show item count badge", () => {});
  });

  describe("when cart is empty", () => {
    it("should display empty cart message", () => {});
    it("should disable checkout button", () => {});
  });

  describe("during checkout process", () => {
    it("should validate stock before proceeding", () => {});
    it("should show loading indicator while processing payment", () => {});
    it("should display success message after completion", () => {});
  });
});

Test Name Checklist#

Before committing your test, verify that its name:

Conclusion#

Thoughtful test naming is a fundamental building block in the broader landscape of writing better tests. To maintain consistency across your team:

  1. Document your naming conventions in detail
  2. Share these guidelines with all team members
  3. Integrate the guidelines into your development workflow

For teams using AI tools like GitHub Copilot:

For more information on linking documentation to Copilot, see: VS Code Experiments Boost AI Copilot Functionality

By following these steps, you can ensure consistent, high-quality test naming across your entire project.

Press Esc or click outside to close

Stay Updated!

Subscribe to my newsletter for more TypeScript, Vue, and web dev insights directly in your inbox.

  • Background information about the articles
  • Weekly Summary of all the interesting blog posts that I read
  • Small tips and trick
Subscribe Now

Most Related Posts