Sefism is free for learners. We are here to help you grow, one step at a time.Join for free
Sefism.

Best practices · Stacks and tools

Software Testing Best Practices

Opens with credits

A practical guide to writing tests that catch real bugs and stay cheap to maintain. It covers choosing the right level for each test, unit test design, mocks and other test doubles, integration and end-to-end tests, flaky tests, coverage and mutation testing, and keeping the suite fast in CI. The advice is language agnostic, with examples in TypeScript and Python.

Written by Tauseef Fayyaz8 chapters, 34 practicesAbout 29 min25 quiz questionsReviewed 13 October 2026

What you will learn

8 chapters · about 29 min

  1. 1Choosing what to test and at which levelEvery test has a cost and a level. This chapter covers the test pyramid, pushing tests to the lowest level that can catch the bug, and testing behaviour instead of implementation.4 practices · 4 min
  2. 2Writing good unit testsA good unit test is short, readable, checks one behaviour and fails with a message that tells you what broke. This chapter covers structure, naming, table driven cases and edge cases.5 practices · 4 min
  3. Test doubles and mockingMocks, stubs and fakes let you test code without its slow or unpredictable dependencies. Used carelessly, they produce tests that pass while the real system is broken.4 practices · 4 minOpens with credits
  4. Integration testsIntegration tests check the places where your code meets something real: a database, a queue, the filesystem or another service. This chapter covers scope, real dependencies and test data.4 practices · 3 minOpens with credits
  5. End-to-end and browser testsEnd-to-end tests give the most confidence and cost the most to run and keep. This chapter covers which journeys deserve them and how to write browser tests that do not break on every change.5 practices · 4 minOpens with credits
  6. Flaky testsA flaky test passes and fails on the same code. This chapter covers why flakiness is so costly, its usual causes, and how to quarantine and fix flaky tests without hiding real bugs.4 practices · 4 minOpens with credits
  7. Coverage and test qualityCoverage tells you which code your tests ran, not whether they checked it. This chapter covers using coverage to find gaps, sensible targets and mutation testing.4 practices · 3 minOpens with credits
  8. Running tests in CI and keeping them fastTests only help if they run on every change and finish quickly. This chapter covers when to run which tests, speed, parallelism and treating test code as real code.4 practices · 3 minOpens with credits

About this guide

These testing best practices are for developers who already write some tests and want a suite they can trust: one that fails when the code is broken, passes when it is not, and runs fast enough that people actually run it. The advice is checked against Martin Fowler's writing on the test pyramid and non-deterministic tests, the Google Testing Blog, and the official documentation for Playwright, Testing Library and pytest.

The ideas are language agnostic. Examples use TypeScript with Vitest or Jest, which share the same describe, it and expect style, Python with pytest, and Playwright for browser tests. The same rules apply to JUnit, Go's testing package or any other runner.

Good tests pay for themselves every time you change code. A clear unit test tells the next developer what a function is supposed to do. A well placed integration test catches the mismatch between your code and the database before a user does. A small set of end-to-end tests proves the main journeys still work after a deploy. Bad tests do the opposite: they break on every refactor, fail at random, and teach the team to ignore red builds.

Start with the first two chapters, on choosing the level for a test and on writing good unit tests, because they shape everything else. Then read the chapters on test doubles, integration tests and end-to-end tests, and finish with flaky tests, coverage and running the suite in CI.

Common questions

What is the difference between unit, integration and end-to-end tests?

A unit test checks one function, class or module on its own and runs in milliseconds. An integration test checks your code together with one real dependency, such as a database or an HTTP client. An end-to-end test runs the whole deployed system, usually through the browser, and checks a complete user journey. Write many unit tests, fewer integration tests and a few end-to-end tests.

What is a good code coverage percentage?

There is no universal number. Google offers 60% as acceptable, 75% as commendable and 90% as exemplary, and says project wide goals above 90% are most likely not worth it while per commit goals for new code can be much higher. Coverage shows what is untested; it does not show that tested code is checked well.

How do I fix a flaky test?

Reproduce it first by running the test many times and running the suite in random order. Then look at the usual causes: shared state between tests, fixed sleeps instead of waiting for a condition, the real clock, remote services and resource leaks. While you fix it, quarantine the test in a non-blocking job with an owner and a ticket.

Should I use mocks in my tests?

Use test doubles at slow or external boundaries such as the network, payment providers, email and the clock. Inside your own code, prefer real collaborators, because tests that mock every class break on every refactor. Prefer checking results and state over checking which calls were made, unless the call itself, such as sending an email, is the outcome.

Is Vitest or Jest better for new projects?

Both use the same describe, it and expect style, so the testing practices are identical. Vitest integrates with Vite and is a common choice for new Vite based projects; Jest is mature and widely used in existing codebases. Pick the one that fits your build tool and team, and spend your effort on writing good tests.

How many end-to-end tests should I have?

As few as protect your critical journeys, such as signing in, the main thing users come for, and anything involving money or permissions. Each end-to-end test is slow and costly to maintain, so check edge cases with unit and integration tests instead and keep a short written list of the journeys your browser tests cover.

Sources (8)

Checked against these, last on 13 October 2026.

  1. The Practical Test Pyramid, martinfowler.commartinfowler.com/articles/practical-test-pyramid.html
  2. Eradicating Non-Determinism in Tests, martinfowler.commartinfowler.com/articles/nonDeterminism.html
  3. Mocks Aren't Stubs, martinfowler.commartinfowler.com/articles/mocksArentStubs.html
  4. Code Coverage Best Practices, Google Testing Blogtesting.googleblog.com/2020/08/code-coverage-best-practices.html
  5. Flaky Tests at Google and How We Mitigate Them, Google Testing Blogtesting.googleblog.com/2016/05/flaky-tests-at-google-and-how-we.html
  6. Best Practices, Playwright docsplaywright.dev/docs/best-practices
  7. Flaky tests, pytest docsdocs.pytest.org/en/stable/explanation/flaky.html
  8. About Queries, Testing Librarytesting-library.com/docs/queries/about/

Picked from the other libraries by shared topics.

Work with me

Stuck on something specific?

Writing only gets you so far. If you want an answer to your situation rather than the general case, book a session and we will work through it together. Sessions are free for approved Sefism members, and a few slots open each week.

  • 1:1 Mentorship
  • Fresh Graduate Mentorship
  • Career Guidance

Brand deals, promos and partnerships: tauseeffayyaz@gmail.com