Labs ICT
Pro Login

Testing Best Practices

Rules that will save you headaches.

Testing Best Practices

Writing tests is one thing. Writing good tests is another. After writing hundreds of tests, I have learned a few patterns that make the difference between tests that help and tests that just slow you down.

Here are the practices that actually matter.

FIRST Principles

Your tests should be Fast, Independent, Repeatable, Self-validating, and Timely. Fast means they run in milliseconds. Independent means one test does not depend on another. Repeatable means they give the same result every time. Self-validating means they pass or fail on their own. Timely means you write them at the right time.


// Good: fast, independent, repeatable
it('adds two numbers', () => {
  expect(add(2, 3)).toBe(5);
});

// Bad: depends on external state
let counter = 0;
it('increments', () => {
  counter++;
  expect(counter).toBe(1);
});
it('increments again', () => {
  counter++;
  expect(counter).toBe(2);
});
    

See the problem with the second example? If the first test fails or runs out of order, the second one fails too. Tests should never depend on execution order.

AAA Pattern

Arrange, Act, Assert. Set up your data, call the function, check the result. It is simple but keeps your tests readable.


it('calculates discount', () => {
  // Arrange
  const price = 100;
  const discountPercent = 20;

  // Act
  const discount = calculateDiscount(price, discountPercent);

  // Assert
  expect(discount).toBe(20);
});
    

Test One Thing

Each test should verify one behavior. If you find yourself writing "and" in your test name, split it into two tests.


// Bad: tests two things
it('validates and saves user', () => {
  const user = createUser('Alice');
  expect(user.isValid).toBe(true);
  expect(saveUser(user)).toBe(true);
});

// Good: each test does one thing
it('validates user name', () => {
  const user = createUser('Alice');
  expect(user.isValid).toBe(true);
});

it('saves valid user', () => {
  const user = createUser('Alice');
  expect(saveUser(user)).toBe(true);
});
    

Avoid Testing Implementation Details

Test what the code does, not how it does it. If you refactor the internal logic but the behavior stays the same, your tests should still pass.

Try it Yourself →

Key Takeaways

  • FIRST: Fast, Independent, Repeatable, Self-validating, Timely
  • AAA: Arrange, Act, Assert pattern keeps tests readable
  • Test one behavior per test case, avoid testing multiple things
  • Never let tests depend on execution order
  • Test behavior, not implementation details