PROGRAMMING-CONCEPTS

Testing: Definition, Purpose, and Examples

Testing is the process of checking whether your code works as intended. It verifies that each part of a program behaves correctly, produces the expected results, and continues to work when you introduce changes. Instead of trusting that code works, testing confirms it through repeatable, automated checks.

Every modern software team relies on testing to catch bugs early, prevent regressions, and build confidence in new features. Whether you’re writing a simple Python function or a large React application, testing is a core skill.


Why Testing Matters

Testing plays a central role across the entire development process:

  • Prevents regressions: New changes don’t break existing behavior.
  • Improves confidence: You can refactor knowing that tests will catch mistakes.
  • Documents behavior: Tests clarify what “correct” means for each function or component.
  • Encourages clean design: Code that’s testable tends to be modular and better structured.
  • Saves time: A failing automated test is faster than debugging production issues.

Testing is not about mistrusting yourself — it’s about ensuring your code remains stable as it grows.


Types of Software Testing

Though testing comes in many variations, developers often focus on three foundational levels.

Unit testing

Tests the smallest pieces of your code, like functions, utilities, or React components.

Goal: Confirm that individual parts behave independently and predictably.

Integration testing

Checks how multiple pieces work together — APIs, databases, modules, or UI segments.

End-to-end (E2E) testing

Simulates real user actions in a full environment.

Goal: Ensure the entire system works from start to finish.

A healthy codebase uses a blend of these methods, not just one.


Unit Testing in JavaScript / TypeScript

Tools like Jest, Vitest, and Mocha make JavaScript testing straightforward.

Example: testing a utility function.

// toFahrenheit.ts
export function toF(c: number): number {
  return (c * 9) / 5 + 32;
}

A basic test:

import { toF } from "./toF";

test("converts Celsius to Fahrenheit", () => {
  expect(toF(0)).toBe(32);
});

This test checks a clear expectation and reports failure if the output changes in the future.


Unit Testing in Python (pytest)

Python’s pytest framework is known for being simple and expressive.

def total(items):
    return sum(items)

Test file:

def test_total():
    assert total([1, 2, 3]) == 6

Pytest automatically discovers test files and runs all test functions.

If behavior changes unexpectedly, pytest highlights exactly where the failure occurred.


Testing Swift Code (XCTest)

Xcode includes XCTest for testing iOS and macOS apps.

Function to test:

func greet(name: String) -> String {
    return "Hello, \(name)"
}

Test:

func testGreet() {
    XCTAssertEqual(greet(name: "Anna"), "Hello, Anna")
}

Swift tests integrate directly into Xcode, showing failures during development and CI pipelines.


Testing React Components

React components benefit from libraries like React Testing Library, which focuses on actual user interactions rather than implementation details.

function Welcome({ name }) {
  return <h1>Hello, {name}</h1>;
}

Test:

import { render, screen } from "@testing-library/react";
import Welcome from "./Welcome";

test("renders name", () => {
  render(<Welcome name="Alice" />);
  expect(screen.getByText("Hello, Alice")).toBeInTheDocument();
});

This checks that the output matches what a user would see.


Mocking Dependencies

Real applications depend on APIs, databases, or external services.

Mocking replaces those dependencies with controlled versions that return predictable values.

JavaScript example:

global.fetch = () =>
  Promise.resolve({
    json: () => Promise.resolve({ name: "Test User" })
  });

Now the test can verify logic without needing a real server.

Python with pytest:

class FakeAPI:
    def get_user(self):
        return {"name": "Test"}

Mocking keeps tests fast and independent of external systems.


Integration Testing

Integration tests confirm that parts of the system work together properly.

Node/TypeScript example (API + logic):

test("GET /user returns user data", async () => {
  const res = await request(app).get("/user");
  expect(res.status).toBe(200);
  expect(res.body.name).toBe("Alice");
});

This ensures that routing, controllers, and data handling all align.

Python example (FastAPI):

def test_get_user(client):
    response = client.get("/user")
    assert response.status_code == 200

Integration tests often use mock databases or temporary test databases.


End-to-End Testing

E2E testing simulates how a user interacts with the actual app.

Common tools: Playwright, Cypress, Selenium.

Example (Playwright):

test("user can log in", async ({ page }) => {
  await page.goto("/login");
  await page.fill("#email", "test@example.com");
  await page.fill("#password", "password");
  await page.click("button[type=submit]");
  await expect(page).toHaveURL("/dashboard");
});

These tests catch issues that unit tests miss — broken forms, bad routing, authentication issues, and layout problems.


Testing in SQL-Driven Applications

Databases require careful testing, but you rarely test SQL directly.

Instead, you test the code that generates or consumes SQL.

Example (testing a repository function in TypeScript):

test("findUser returns null for unknown ID", async () => {
  db.query = jest.fn().mockResolvedValue([]);
  const result = await findUser(999);
  expect(result).toBeNull();
});

This isolates your logic from the real database while ensuring correctness.


Test Coverage and What It Really Means

Coverage tools measure how much of your code is executed by tests.

High coverage is useful, but misleading if:

  • Tests don’t check meaningful assertions
  • Code executes but isn’t validated
  • Critical paths remain untested

Coverage is a tool, not a goal. Good tests matter more than a high percentage.


Real-World Example: Testing a Data Processing Function

Python example that validates behavior with edge cases:

def average(nums):
    if not nums:
        return 0
    return sum(nums) / len(nums)

Tests:

def test_average_basic():
    assert average([10, 20]) == 15

def test_average_empty():
    assert average([]) == 0  # edge case

Covering normal and edge cases prevents surprises later.


Common Testing Mistakes

  • Over-testing: Too many tests slow development without adding value.
  • Under-testing: Missing edge cases or skipping integration paths.
  • Testing implementation instead of behavior: Leads to fragile tests.
  • Not isolating dependencies: Makes tests slow and flaky.
  • Skipping tests for error-handling paths: These often hide the most bugs.

Healthy testing balances clarity, speed, and coverage.


Best Practices

  • Write small, clear tests that validate one thing at a time
  • Prefer behavior-driven assertions (“outputs should match expectations”)
  • Use mocks to control unpredictable external behavior
  • Include meaningful edge cases
  • Keep tests fast to encourage frequent execution
  • Treat tests as part of the codebase — maintain them as carefully as production code

Strong testing habits make the entire development process smoother and more reliable.


Summary

Testing is the practice of verifying that code behaves correctly and consistently. It includes unit tests for small pieces, integration tests for combined components, and end-to-end tests for real user flows. Whether you’re writing functions in Python, handling API logic in TypeScript, or building React interfaces, testing ensures stability, prevents regressions, and supports confident development. Good tests serve as documentation, safety nets, and design guides all at once.

Learn to Code for Free
Start learning now
button icon
To advance beyond this tutorial and learn to code by doing, try the interactive experience of Mimo. Whether you're starting from scratch or brushing up your coding skills, Mimo helps you take your coding journey above and beyond.

Sign up or download Mimo from the App Store or Google Play to enhance your programming skills and prepare for a career in tech.

Reach your coding goals faster