Software Testing Unit Testing
Unit testing is the process of testing the smallest individual pieces of code — called units — in complete isolation. A unit is usually a single function or method. The goal is to verify that each unit does exactly what it is supposed to do, on its own, before it is combined with other units.
What Is a Unit?
A full e-commerce application has many units: ┌───────────────────────────────────────────────────────┐ │ calculate_total(price, quantity) │ │ apply_discount(total, discount_code) │ │ validate_email(email_string) │ │ format_date(date_object) │ │ check_stock(product_id) │ └───────────────────────────────────────────────────────┘ Each function is a unit. Each unit is tested separately.
Why Test Units in Isolation?
When a big system fails, finding the cause is difficult if everything is connected. Testing each unit alone makes it easy to pinpoint exactly which piece of code has a problem.
Full system test fails → "Something is wrong somewhere" Unit test fails → "The calculate_discount function is broken"
Anatomy of a Unit Test
Every unit test follows the Arrange → Act → Assert pattern.
ARRANGE: Set up the inputs and any required conditions
price = 500
quantity = 3
ACT: Call the function being tested
result = calculate_total(price, quantity)
ASSERT: Check that the output is correct
assert result == 1500
A Clear Unit Test Example
Function being tested: is_adult(age) — returns True if age is 18 or above, False otherwise.
Test 1: is_adult(18) → Expected: True → Actual: True → PASS Test 2: is_adult(17) → Expected: False → Actual: False → PASS Test 3: is_adult(0) → Expected: False → Actual: False → PASS Test 4: is_adult(-1) → Expected: False → Actual: True → FAIL ← Bug found!
Without the unit test, a negative age would be incorrectly treated as an adult. The test found this bug early, before it reached any integrated system.
Test Doubles: Mocks and Stubs
A unit must be tested in isolation. But some units depend on other components — like a database or an external payment service. Test doubles replace those dependencies so the unit can run alone.
Stub
A stub returns a fixed response when called. Instead of calling the real payment gateway, the test uses a stub that always returns "Payment approved."
Real function: charge_card() → contacts bank server
Stub: charge_card() → always returns {"status": "approved"}
The unit being tested works with the stub instead.
Mock
A mock is a more advanced stub. It also records how it was called — how many times, with what arguments — so the test can verify not just the output, but also that the right calls were made.
Mock records: - Was send_email() called once? ✔ - Was it called with the right email address? ✔ - Was it called at all? ✔
Popular Unit Testing Frameworks
LANGUAGE FRAMEWORK ──────── ───────── Python pytest, unittest JavaScript Jest, Mocha Java JUnit, TestNG C# NUnit, xUnit PHP PHPUnit Ruby RSpec
Code Coverage in Unit Testing
Code coverage measures what percentage of the code is executed by unit tests. A coverage of 80% means 80% of code lines were run during testing.
┌─────────────────────────────────────────┐ │ Total lines of code: 200 │ │ Lines covered by tests: 160 │ │ Coverage: 80% │ └─────────────────────────────────────────┘
High coverage does not automatically mean good tests. A test that runs every line but checks nothing is useless. Coverage is a guide, not a guarantee.
Benefits of Unit Testing
- Bugs are found at the earliest possible stage — in individual functions.
- Refactoring code becomes safer — existing unit tests catch regressions immediately.
- Well-written unit tests serve as living documentation for what each function is supposed to do.
- Developers gain confidence to make changes without fear of unknowingly breaking other parts.
Unit Testing in the Testing Pyramid
/\
/ \
/ UI \ ← Few, slow, expensive
/──────\
/ Service\ ← Medium number
/──────────\
/ Unit Tests \ ← Many, fast, cheap
────────────────
Unit tests form the wide, stable base of the pyramid.
The testing pyramid recommends having many fast unit tests at the base, fewer integration tests in the middle, and a small number of end-to-end UI tests at the top. Unit tests give the most value at the lowest cost.
