Home/Coding & Tech Skills

Unit Testing Basics: 5 Steps Every New Developer Needs in 2026

coding-tech-skills · Coding & Tech Skills

I remember staring at my first codebase as a junior developer — a sprawling Node.js API with no tests. Every time I made a change, I’d manually hit endpoints in Postman, squint at the response, and pray nothing else broke. When I finally pushed a commit that crashed the login service for an hour, my senior just sighed and said, “You need unit tests.” But learning unit testing basics felt like being handed a foreign language dictionary with no grammar guide. Mocks, stubs, assertions, coverage — the jargon alone made me want to close the tab. If that sounds familiar, you’re not alone. The good news is that in 2026, the tooling has matured enough that any new developer can get from zero to a passing test in under 20 minutes. This guide skips the theory overload and gives you five concrete steps that actually work. By the end, you’ll have written your first test, understood the patterns that keep tests readable, and automated the whole thing so you never forget to run them again.

Step 1: Pick Your Testing Framework and Write Your First Test (It’s Simpler Than You Think)

The biggest mistake I made early on was trying to learn a framework by reading its entire documentation. Don’t do that. Instead, pick the most beginner-friendly option for your language. For JavaScript/TypeScript in 2026, that’s Jest — it comes with zero config for most projects, includes built-in assertions and mocking, and has a massive community. For Python, it’s pytest — its plain assert statements feel like writing normal code. For C#, xUnit is clean and well-supported.

Here’s the exact script I use when teaching new developers. Open your terminal, install Jest (npm install --save-dev jest), then create a file called sum.js with a simple function:

function sum(a, b) { return a + b; }
module.exports = sum;

Now create sum.test.js:

const sum = require('./sum');
test('adds 1 + 2 to equal 3', () => {
  expect(sum(1, 2)).toBe(3);
});

Run npx jest. If you see a green PASS, you just wrote your first unit test. That’s it. No magic, no complex setup. This step builds momentum because you instantly see a result. I’ve seen developers go from intimidated to grinning in under two minutes. The key is to keep the first test trivial — you’re not testing real logic yet; you’re proving the system works and learning the syntax.

Step 2: Master the Three A’s – Arrange, Act, Assert – for Readable Tests

Once you’ve got a passing test, the next trap is writing tests that are a mess of inline setup and comments. The remedy is the Arrange-Act-Assert pattern, which I wish someone had drilled into me on day one. It’s a simple structure that makes your tests read like a story.

In my own setup, I was testing a user registration function. Early tests looked like this — a jumble of setup, action, and checks all mixed together:

test('registers user', () => {
  const db = connectToTestDatabase();
  const user = createUser('alice', 'pass123');
  const result = saveUserToDB(user);
  expect(result.success).toBe(true);
  expect(db.findUser('alice').name).toBe('alice');
});

That works, but it’s hard to scan. After refactoring with AAA:

test('registers a new user successfully', () => {
  // Arrange
  const input = { username: 'alice', password: 'pass123' };
  const db = connectToTestDatabase();

  // Act
  const result = saveUserToDB(input);

  // Assert
  expect(result.success).toBe(true);
  const savedUser = db.findUser('alice');
  expect(savedUser.username).toBe('alice');
});

The difference is night and day. Now each section has a clear purpose. When a test fails, you can immediately see whether the problem is in the setup, the action, or the expectation. This pattern is universal — it works across Jest, pytest, xUnit, and every other framework. Make it your default, and your teammates will thank you.

Step 3: Test One Thing at a Time – How to Avoid the ‘Giant Test’ Trap

I once inherited a test that was over 200 lines long. It created a user, logged them in, posted a comment, edited the comment, deleted it, and then checked the database. When it failed, I had no idea which step broke. That’s the giant test trap, and it’s the fastest way to make unit tests useless.

The principle is simple: one test, one behavior. A function like loginUser might have multiple behaviors — valid credentials, invalid password, missing username, account locked. Each of those deserves its own test. Here’s a bad example that tries to test two things at once:

test('login works', () => {
  const result = login('alice', 'correct_password');
  expect(result.success).toBe(true);
  const failed = login('alice', 'wrong_password');
  expect(failed.success).toBe(false);
});

If the second assertion fails, you don’t know if the first passed or not. Worse, refactoring the login function later might require changing both cases. Instead, split them:

test('login succeeds with valid credentials', () => {
  const result = login('alice', 'correct_password');
  expect(result.success).toBe(true);
});

test('login fails with invalid password', () => {
  const result = login('alice', 'wrong_password');
  expect(result.success).toBe(false);
});

Now each test stands alone. When one fails, you know exactly what broke. This also makes your test suite a living documentation of what the code should do. New developers on your team can read the test names and understand the expected behavior without digging into implementation details.

Step 4: Use Mocks and Stubs to Isolate Your Code (Without Getting Overwhelmed)

Mocking scared me for months. I thought it required deep framework knowledge and would break my tests in mysterious ways. In reality, mocking is just replacing a real dependency with a pretend one that returns controlled values. You need it when your code talks to something outside your control — a database, an API, a file system — because those make tests slow and unreliable.

Let’s say you have a function that fetches weather data from an external API. You don’t want your unit tests to actually call that API (it might be down, slow, or cost money). With Jest, mocking is a one-liner. Here’s a concrete example from a project I worked on:

// weatherService.js
const api = require('./api');
async function getTemperature(city) {
  const data = await api.fetchWeather(city);
  return data.temperature;
}

// weatherService.test.js
jest.mock('./api');
const api = require('./api');
const getTemperature = require('./weatherService');

test('returns temperature for a city', async () => {
  api.fetchWeather.mockResolvedValue({ temperature: 22 });
  const temp = await getTemperature('London');
  expect(temp).toBe(22);
});

See what happened? jest.mock('./api') replaces the real module with a fake one. Then mockResolvedValue tells the fake to return 22 degrees. The test runs in milliseconds, and it doesn’t care if the real API is offline. A common beginner mistake is mocking everything in sight, even pure functions. My rule of thumb: mock only external I/O. If it’s a pure calculation, just test it directly with real inputs and outputs.

Step 5: Run Your Tests Automatically – Set Up a One-Command Workflow in 10 Minutes

Writing tests is useless if you never run them. The best habit I ever built was making test execution a no-brainer. In 2026, every language ecosystem has a standard test command. For Node.js projects, it’s npm test (which runs Jest). For Python, it’s pytest. For C#, it’s dotnet test.

But you can go a step further. Add a pre-commit hook that runs your tests before every commit. Tools like Husky (for JavaScript) or pre-commit (for Python) make this trivial. Here’s a quick setup with Husky:

  1. Install Husky: npm install --save-dev husky
  2. Enable hooks: npx husky install
  3. Add a test hook: npx husky add .husky/pre-commit "npm test"

Now every time you commit, your tests run automatically. If they fail, the commit is blocked. In my own workflow, this prevented at least a dozen embarrassing push-to-main moments in the first month alone. If you’re on a team, also set up a CI pipeline (GitHub Actions, GitLab CI, CircleCI) that runs tests on every pull request. The setup takes about 10 minutes, and it buys you permanent peace of mind.

Conclusion: You’re Now Ready to Write Tests That Save You Hours (Not Waste Them)

Unit testing basics don’t have to be overwhelming. Pick a framework and write one trivial test. Structure every test with Arrange-Act-Assert. Test one behavior per test. Mock only external dependencies. And automate the execution so you never think about it again. I’ve seen developers go from dreading tests to writing them before code, because they realized tests make refactoring fearless and debugging faster. Start with a small project — maybe a utility function you already use — and apply these five steps. Within a week, you’ll wonder how you ever coded without them.