DEV Community

Cover image for Software Testing Basics: The One Habit That Matters More Than Any Tool
Marx Jenes
Marx Jenes

Posted on Edited on

Software Testing Basics: The One Habit That Matters More Than Any Tool

A junior dev I mentored a while back asked me which testing framework he should learn first. Reasonable question. I told him it didn't matter much yet, and he looked at me like I'd dodged it. I hadn't. The honest answer is that the framework was the least important thing standing between him and writing tests that actually caught bugs.

The thing that actually mattered - the one habit I've seen separate people who write tests that catch real problems from people who write tests that just exist - is stupidly simple: write down what you expect to happen before you write the test that checks it. Not "test the login function." What, specifically, should happen when the password is wrong. What should happen when the account doesn't exist. What should happen when someone hits submit twice.

Why this sounds too obvious to matter

Most people already think they do this. They don't, not really. What actually happens most of the time is: write the code, run it, see what it does, write a test that confirms it does that. That's not testing. That's documentation of current behavior, and current behavior includes your bugs.

The habit is inverted from that. You write down the expected result before you look at what the code actually does, ideally before the code is even fully written. It feels slower. It's the entire difference between a test suite that catches regressions and one that just quietly agrees with whatever's already there.

Where I've seen this go wrong

Early in my career I tested a password reset flow by running it, watching it work, and writing an assertion that matched what I saw. Six months later someone changed an error message and my test broke - correctly, technically, since the output changed, but I couldn't tell if the new behavior was fine or actually wrong, because I'd never written down what "right" was supposed to look like. I'd only ever recorded what "current" looked like. Three of us argued about it in a PR review for twenty minutes before someone just asked product what the message was supposed to say in the first place, which is the question I should have answered before writing the test at all.

This is the same failure mode as confusing verification and validation - checking that code matches what it currently does isn't the same as checking that it does what it's actually supposed to do. The habit of writing the expected result down first is what forces that distinction to actually show up, instead of staying invisible until something breaks in a confusing way.

What this looks like in practice

Before writing a test, three quick questions, in this order:

What should happen here, in plain language, no code? ("Login fails with a clear error and no session is created" - not "returns 401.")
What's the one thing that would make this wrong even if it technically ran without crashing? (A silent failure. A vague error. Data left in a bad state.)
Would someone unfamiliar with this code understand what failed and why, just from the test name and assertion?

If you can't answer #1 without looking at the code first, that's the actual signal something's off - not the framework, not the tooling, not the coverage number.

Why this matters more than tool choice

Frameworks, runners, assertion libraries - these are genuinely interchangeable in a way that surprises people early on. Jest and Vitest and pytest all do fundamentally the same job with different syntax. Nobody's test suite got meaningfully better because they switched frameworks. Suites get better when the people writing them get sharper about defining "correct" before checking it.

This is also, not coincidentally, the actual foundation under most of what gets called software testing basics- the types of testing, the levels, the terminology. All of it assumes you already know what you're checking for. None of the vocabulary helps if that part's fuzzy. I found Keploy's rundown of the fundamentals genuinely useful for tying that vocabulary together in one place, if you want the fuller map of levels, types, and lifecycle beyond just this one habit.

Where it pays off later

This habit compounds in a way tool choice never does. A test suite where every test was written against a clearly stated expectation is a suite people trust - when something turns red, the team knows immediately whether it's a real regression or an intentional behavior change, instead of spending twenty minutes in a PR thread trying to reconstruct what "right" was supposed to mean.

Learn a framework whenever you need to. Learn to write the expected result down first, before you touch the code, and you'll write better tests in whatever framework you're handed next.

Top comments (0)