Best practices · Stacks and tools
Software Testing Best Practices
Opens with creditsA 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.
What you will learn
8 chapters · about 29 min
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
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
- 1Structure every test as Arrange, Act, Assert
- 2Test one behaviour per test and name it after that behaviour
- 3Use table driven tests for many inputs to the same logic
- 4Prefer readable tests over clever, deduplicated ones
- 5Test boundaries and failure paths, not only the happy path
5 in this chapterOpen this chapter- 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
- 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
- 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
- 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
- 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
- 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.
- The Practical Test Pyramid, martinfowler.commartinfowler.com/articles/practical-test-pyramid.html
- Eradicating Non-Determinism in Tests, martinfowler.commartinfowler.com/articles/nonDeterminism.html
- Mocks Aren't Stubs, martinfowler.commartinfowler.com/articles/mocksArentStubs.html
- Code Coverage Best Practices, Google Testing Blogtesting.googleblog.com/2020/08/code-coverage-best-practices.html
- Flaky Tests at Google and How We Mitigate Them, Google Testing Blogtesting.googleblog.com/2016/05/flaky-tests-at-google-and-how-we.html
- Best Practices, Playwright docsplaywright.dev/docs/best-practices
- Flaky tests, pytest docsdocs.pytest.org/en/stable/explanation/flaky.html
- About Queries, Testing Librarytesting-library.com/docs/queries/about/
Related on Sefism
Picked from the other libraries by shared topics.
Cheat sheets
See allBest practices
See allChecklists
See all- LLM Evaluation ChecklistAn LLM evaluation checklist: success criteria, eval datasets, code and model graders, judge calibration, agent evals, safety tests and production monitoring, with how to verify each check.
- Data Quality ChecklistA data quality checklist for warehouse tables: ownership, freshness, volume, schema, nulls, uniqueness, referential checks, anomaly monitoring and data incidents, with how to verify each.
Project structures
See all- Flutter project structureFor anyone building one Dart app for Android, iOS and the web. The tree starts from what flutter create gives you and grows into a feature-first lib/ folder, where each feature keeps its own data, domain and presentation code, then adds tests at every level, flavors, release signing, crash reporting and, for large teams, a melos workspace of local packages.
- Android with Kotlin and Compose project structureFor anyone building a native Android app the way Google recommends in 2026: Kotlin, Jetpack Compose, a ViewModel per screen, repositories over Room and the network, and Hilt wiring it together. The tree starts from Android Studio's Empty Activity template, grows into feature packages with data, domain and presentation, adds signing, shrinking, CI and crash reporting for production, and ends as a multi-module build with convention plugins, Baseline Profiles and staged rollouts.
- iOS app with SwiftUI project structureFor students building a native iPhone app in Swift. The tree starts from Xcode's App template and grows into feature folders with @Observable models, a small URLSession API client, a SwiftData offline cache and Swift Testing, then adds build configurations, privacy manifests, fastlane or Xcode Cloud releases and, for large teams, local Swift packages.
Career questions
See all- How do I know if code written by an AI tool is actually correct?You check it the same way you check any code, but more carefully: read the whole diff, confirm every function and package exists, run it against tests that could fail, especially one you wrote yourself, try the edge cases, and make sure you can explain every line. Correct means proven by evidence, not "the tool said it works".
- How do I move from QA to software development?Use test automation as the bridge. Write more and better code in your QA role, pick one development stack that matches your product, take small development tasks inside your current team, and ask for an internal move before you apply outside. Your testing mindset is an advantage, not something to hide.
- What does an AI evals engineer do?An AI evals engineer designs the tests that tell a team whether an AI system is good enough: the datasets, the scoring (code checks, human review, or a model as a judge), and the process that runs them on every change. It is a real and growing specialism inside AI engineering, and a strong angle for juniors because few people do it well.
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