My App
React

Testing Fundamentals — Real World Examples from the Task Tracker Suite

Learn Unit, Integration, and E2E testing with real-world examples from the Task Tracker Testing Suite project using FastAPI, PyTest, and React Testing Library.

🧪 Testing Fundamentals

Understanding Testing Layers with Real World Examples


🧠 Overview

Testing ensures that your project works correctly and remains stable when you make changes or add new features.

In the Task Tracker Testing Suite, we tested:

  • FastAPI backend
  • SQLite database
  • React frontend

Tools used:

  • 🧩 PyTest for backend testing
  • ⚛️ Jest & React Testing Library for frontend testing

Real-world testing helps prevent bugs, reduce manual checks, and build developer confidence before deployment.


🧩 1. The Testing Pyramid

The Testing Pyramid represents how different types of tests are layered in your project.


Testing Pyramid Overview:

End-to-End (E2E) Tests
    🔹 Tests full application flow and real user experience

⬆️ Integration Tests
    🔹 Ensure different modules (API, DB, frontend) work together

⬆️ Unit Tests
    🔹 Verify small, individual functions or routes

Base of the Pyramid


💡 Real-World Example:
In our Task Tracker App:

  • Unit tests verify that /tasks API can create or return tasks.
  • Integration tests check if FastAPI connects to SQLite properly.
  • E2E tests simulate a user adding tasks from the React frontend and seeing them appear in the list.

The goal is to have many unit tests, fewer integration tests, and only a few E2E tests
to keep testing fast and effective.


🔹 Unit Testing

Definition:
Unit tests verify small, isolated parts of your code — such as a single function or route.

Example (FastAPI):

def test_create_task():
    response = client.post("/tasks?title=Buy Milk")
    assert response.status_code == 200
    data = response.json()
    assert data["title"] == "Buy Milk"

💡 Real-World Example: When you run this test, it only checks that the API correctly creates a new task — it doesn’t involve the database or frontend.

For example, Amazon or Netflix developers use similar unit tests to confirm that “Add to Cart” or “Like” buttons work correctly at the function level.


🔹 Integration Testing

Definition: Integration tests check how multiple parts of the app — such as APIs, models, and databases — work together.

Example (FastAPI + SQLAlchemy):

def test_database_connection():
    db = SessionLocal()
    new_task = Task(title="Integration Test")
    db.add(new_task)
    db.commit()
    task_in_db = db.query(Task).filter_by(title="Integration Test").first()
    assert task_in_db is not None
    db.close()

💡 Real-World Example: When you add a task in your app, the data moves from the API → database → back to API response. Integration testing ensures that these layers communicate perfectly. This is like ensuring that when you send a payment request in a shopping app, it updates both the “Orders” and “Payments” tables successfully.


🔹 End-to-End (E2E) Testing

Definition: E2E tests simulate how real users interact with your application. They ensure that all systems — frontend, backend, and database — work smoothly together.

Example Flow (React + FastAPI):

  1. User opens Task Tracker
  2. Types “Buy Groceries” in the input
  3. Clicks the “Add” button
  4. The app calls FastAPI and displays the new task

💡 Real-World Example: E2E testing is similar to how testers at Zomato or Swiggy verify that a user can search for food, add it to the cart, and place an order — covering all systems end-to-end.


⚙️ 2. PyTest Fixtures and Parametrization

🔸 Fixtures

Definition: Fixtures are reusable setups that prepare your environment before each test. For example, creating and closing database sessions automatically.

Example:

import pytest
from database import SessionLocal

@pytest.fixture
def db():
    db = SessionLocal()
    yield db
    db.close()

Now, reuse this fixture in your tests:

def test_add_task(db):
    new_task = Task(title="Learn PyTest")
    db.add(new_task)
    db.commit()
    assert new_task.id is not None

💡 Real-World Example: Just like how Amazon sets up a temporary test cart to verify adding/removing items, fixtures give you a “safe sandbox” to run tests without affecting the main database.


🔸 Parametrization

Definition: Parametrization lets you test multiple inputs in one test function.

Example:

import pytest

@pytest.mark.parametrize("title", ["Task A", "Task B", "Task C"])
def test_create_multiple_tasks(title):
    response = client.post(f"/tasks?title={title}")
    assert response.status_code == 200

💡 Real-World Example: If your system supports multiple categories (Work, Personal, Study), parametrized tests help confirm that all work without repeating the same code.


⚛️ 3. React Testing Library Philosophy

The React Testing Library (RTL) helps test components the way a user interacts with them. It focuses on behavior, not implementation.

Key Idea:

Test your app as a user would use it.

Example:

test("adds a new task", async () => {
  render(<TaskList />);
  fireEvent.change(screen.getByPlaceholderText("Enter task"), {
    target: { value: "Buy Milk" },
  });
  fireEvent.click(screen.getByText("Add"));
  const newTask = await screen.findByText("Buy Milk");
  expect(newTask).toBeInTheDocument();
});

💡 Real-World Example: When you press “Post” on Instagram, React Testing Library ensures the post appears instantly on screen — just like our Task Tracker app confirms that “Buy Milk” shows up after clicking “Add.”


🧩 4. Mocking Strategies

Definition: Mocking replaces real dependencies (like APIs or databases) with fake ones during testing. This makes your tests faster and safer.

Example:

jest.mock("../api", () => ({
  getTasks: jest.fn(() => Promise.resolve({ data: [] })),
  createTask: jest.fn(() => Promise.resolve({ data: { id: 1, title: "Buy Milk" } })),
}));

💡 Real-World Example: Just like a banking app uses test servers for payments, mocking allows your code to “pretend” an API is working — without calling the real server.


🧠 5. Test-Driven Development (TDD)

Definition: TDD means writing tests before writing actual code. This ensures your logic meets the expected behavior.

🔹 TDD Workflow

  1. 🟥 Write a failing test (Red)
  2. 🟩 Write code to make it pass (Green)
  3. 🧹 Refactor and clean up (Refactor)

Example:

# Step 1: Write the test first
def test_create_task():
    response = client.post("/tasks?title=Learn TDD")
    assert response.status_code == 200

Now run it — it fails (no route yet).

Then create the route:

@app.post("/tasks")
def create_task(title: str):
    return {"title": title}

Run tests again — ✅ it passes!

💡 Real-World Example: Companies like Google and Microsoft use TDD heavily — ensuring every new feature (like “search” or “upload”) already has a test written before coding.


🧾 Summary

ConceptDescription
Unit TestsTest small, isolated functions or APIs
Integration TestsTest database and API together
E2E TestsSimulate real user behavior
FixturesReusable environment setup for tests
ParametrizationRun one test with multiple inputs
MockingReplace real dependencies with fake ones
TDDWrite tests before implementing features

💡 Real-World Insight

In the Task Tracker Testing Suite, these methods ensured that:

  • Every API endpoint worked properly
  • The database stored and retrieved data correctly
  • The frontend displayed tasks as expected
  • The entire system worked together smoothly

Testing isn’t just about finding bugs — it’s about building confidence that your code works under any condition.

That’s how real-world software teams at companies like Google, Amazon, and Netflix maintain high reliability even after hundreds of daily deployments.