12 Prompts for Writing Unit and Integration Tests

Introduction

Writing tests is the cornerstone of maintainable software. But staring at a blank test file can be daunting. This collection of 12 battle-tested prompts helps you generate unit and integration tests for Python (pytest, unittest) and JavaScript (Jest). Each prompt is a complete template you can adapt immediately.

How to Use These Prompts

Replace placeholders like {function_name} or {class_name} with your actual code. The prompts are designed to work with AI assistants (ChatGPT, Claude, GitHub Copilot) or as manual checklists.

1. Basic Function Unit Test (pytest)

Prompt: "Generate pytest tests for the following function. Include edge cases (empty input, None, type mismatches) and a parametrized test."

# Example function
def divide(a: float, b: float) -> float:
    if b == 0:
        raise ValueError("Cannot divide by zero")
    return a / b

# Generated test
import pytest

def test_divide_positive():
    assert divide(10, 2) == 5.0

def test_divide_negative():
    assert divide(-6, 3) == -2.0

def test_divide_by_zero():
    with pytest.raises(ValueError, match="Cannot divide by zero"):
        divide(5, 0)

@pytest.mark.parametrize("a,b,expected", [
    (0, 1, 0.0),
    (1, 3, 1/3),
    (100, 0.5, 200.0)
])
def test_divide_parametrized(a, b, expected):
    assert divide(a, b) == pytest.approx(expected)

Why it works: Covers happy path, error handling, and floating-point precision with approx.

2. Complex Business Logic (unittest)

Prompt: "Create unittest tests for a class that processes orders. Mock the database call and test each method: create_order, cancel_order, get_order_total."

from unittest import TestCase, mock
from order_service import OrderService

class TestOrderService(TestCase):
    def setUp(self):
        self.service = OrderService()

    @mock.patch('order_service.db')
    def test_create_order_success(self, mock_db):
        mock_db.insert.return_value = 123
        result = self.service.create_order({'item': 'laptop', 'price': 1500})
        self.assertEqual(result['order_id'], 123)
        mock_db.insert.assert_called_once()

    @mock.patch('order_service.db')
    def test_cancel_order_twice(self, mock_db):
        mock_db.find.return_value = {'status': 'cancelled'}
        with self.assertRaises(RuntimeError):
            self.service.cancel_order(1)

Why it works: Mocks isolate the class, setUp avoids duplication.

3. API Endpoint Integration Test (pytest + requests)

Prompt: "Write pytest integration tests for a REST API endpoint /users/{id}. Test 200, 404, and 422 status codes."

import pytest
import requests

BASE_URL = "https://api.example.com/v1"

@pytest.mark.integration
def test_get_user_exists():
    resp = requests.get(f"{BASE_URL}/users/1")
    assert resp.status_code == 200
    data = resp.json()
    assert "id" in data
    assert "name" in data

@pytest.mark.integration
def test_get_user_not_found():
    resp = requests.get(f"{BASE_URL}/users/99999")
    assert resp.status_code == 404

@pytest.mark.integration
def test_invalid_user_id():
    resp = requests.get(f"{BASE_URL}/users/abc")
    assert resp.status_code == 422

Real‑world tip: Use environment variables for BASE_URL. Marking tests with @pytest.mark.integration allows running them separately via pytest -m integration.

4. Database Integration Test (pytest + SQLite)

Prompt: "Create pytest tests for a DAO class that uses SQLite. Use an in‑memory database and test CRUD operations."

import pytest
import sqlite3
from myapp.dao import UserDAO

@pytest.fixture
def db_conn():
    conn = sqlite3.connect(":memory:")
    conn.execute("CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, email TEXT)")
    conn.commit()
    yield conn
    conn.close()

@pytest.fixture
def dao(db_conn):
    return UserDAO(db_conn)

def test_insert_and_fetch(dao):
    user_id = dao.insert("Alice", "alice@example.com")
    user = dao.fetch(user_id)
    assert user.name == "Alice"

def test_fetch_nonexistent(dao):
    assert dao.fetch(999) is None

Why it works: In‑memory SQLite is fast and isolated. Fixtures ensure clean state per test.

5. JavaScript Async Function Test (Jest)

Prompt: "Write Jest tests for an async function that fetches data from an external API. Mock the fetch call."

// Module: userService.js
export const getUser = async (id) => {
  const response = await fetch(`https://api.example.com/users/${id}`);
  if (!response.ok) {
    throw new Error('User not found');
  }
  return response.json();
};

// Test file
import { getUser } from './userService';

global.fetch = jest.fn();

test('fetches user successfully', async () => {
  fetch.mockResolvedValueOnce({
    ok: true,
    json: async () => ({ id: 1, name: 'Alice' })
  });
  const user = await getUser(1);
  expect(user.name).toBe('Alice');
});

test('throws on 404', async () => {
  fetch.mockResolvedValueOnce({ ok: false });
  await expect(getUser(999)).rejects.toThrow('User not found');
});

Why it works: Mocks prevent real network calls. rejects.toThrow checks error handling.

6. React Component Test (Jest + Testing Library)

Prompt: "Write a Jest test for a React LoginForm component. Check that it calls onSubmit with correct data and shows validation errors."

import { render, screen, fireEvent } from '@testing-library/react';
import LoginForm from './LoginForm';

test('submits form with email and password', () => {
  const mockSubmit = jest.fn();
  render(<LoginForm onSubmit={mockSubmit} />);
  fireEvent.change(screen.getByLabelText(/email/i), {
    target: { value: 'test@example.com' }
  });
  fireEvent.change(screen.getByLabelText(/password/i), {
    target: { value: '123456' }
  });
  fireEvent.click(screen.getByRole('button', { name: /submit/i }));
  expect(mockSubmit).toHaveBeenCalledWith({
    email: 'test@example.com',
    password: '123456'
  });
});

Real‑world tip: Use data-testid attributes for hard‑to‑find elements.

7. Class with Dependencies (unittest mock)

Prompt: "Write tests for a class that depends on an external payment gateway. Mock the gateway and test the payment flow."

from unittest import TestCase, mock
from payment import PaymentProcessor

class TestPaymentProcessor(TestCase):
    @mock.patch('payment.GatewayClient')
    def test_charge_success(self, MockGateway):
        mock_gw = MockGateway.return_value
        mock_gw.charge.return_value = {'status': 'success', 'txn_id': 'abc123'}
        processor = PaymentProcessor(mock_gw)
        result = processor.charge(100, 'tok_visa')
        self.assertEqual(result['status'], 'success')
        mock_gw.charge.assert_called_once_with(100, 'tok_visa')

Why it works: Patching at the import level (payment.GatewayClient) replaces the entire class with a mock.

8. Data Transformation Test (pytest)

Prompt: "Write parametrized tests for a function that normalizes phone numbers. Include international, local, and invalid formats."

import pytest
from utils import normalize_phone

@pytest.mark.parametrize("input,expected", [
    ("+1 (555) 123-4567", "+15551234567"),
    ("555.123.4567", "+15551234567"),
    ("+44 20 7946 0958", "+442079460958"),
    pytest.param("abc", None, marks=pytest.mark.xfail(strict=False, reason="non‑numeric"))
])
def test_normalize_phone(input, expected):
    assert normalize_phone(input) == expected

Why it works: Parametrization covers many cases compactly. xfail marks expected failures without failing the test suite.

9. File I/O Test (pytest + tmp_path)

Prompt: "Write pytest tests for a function that reads a configuration file. Use tmp_path fixture to create temp files."

import pytest
import json
from config_loader import load_config

def test_load_valid_config(tmp_path):
    d = tmp_path / "config.json"
    d.write_text('{"db": "sqlite:///test.db"}')
    config = load_config(str(d))
    assert config["db"] == "sqlite:///test.db"

def test_load_missing_file(tmp_path):
    with pytest.raises(FileNotFoundError):
        load_config(str(tmp_path / "nonexistent.json"))

def test_load_invalid_json(tmp_path):
    d = tmp_path / "config.json"
    d.write_text("not json")
    with pytest.raises(json.JSONDecodeError):
        load_config(str(d))

Why it works: tmp_path is automatically cleaned up, no manual file deletion.

10. Jest Snapshot Test (React)

Prompt: "Write a Jest snapshot test for a UserProfile component to catch unintended UI changes."

import renderer from 'react-test-renderer';
import UserProfile from './UserProfile';

test('renders correctly', () => {
  const tree = renderer
    .create(<UserProfile name="Alice" bio="Developer" />)
    .toJSON();
  expect(tree).toMatchSnapshot();
});

Real‑world tip: Always review snapshot diffs during code review. Update with jest --updateSnapshot only after intentional changes.

11. Integration Test with Docker (pytest + testcontainers)

Prompt: "Write a pytest integration test that spins up a PostgreSQL container with testcontainers and runs queries."

import pytest
from testcontainers.postgres import PostgresContainer

@pytest.fixture(scope="module")
def postgres_container():
    with PostgresContainer("postgres:16") as pg:
        yield pg

def test_crud(postgres_container):
    from sqlalchemy import create_engine, text
    engine = create_engine(postgres_container.get_connection_url())
    with engine.connect() as conn:
        conn.execute(text("CREATE TABLE items (id SERIAL PRIMARY KEY, name VARCHAR)"))
        conn.execute(text("INSERT INTO items (name) VALUES ('test')"))
        result = conn.execute(text("SELECT name FROM items"))
        assert result.fetchone()[0] == 'test'

Why it works: Container lifecycle is managed; module‑scope reduces overhead.

12. End‑to‑End API Integration (Jest + supertest)

Prompt: "Write Jest integration tests for an Express API using supertest. Test POST /login and GET /protected."

const request = require('supertest');
const app = require('../app');

describe('POST /login', () => {
  it('returns token for valid credentials', async () => {
    const res = await request(app)
      .post('/login')
      .send({ username: 'admin', password: 'secret' });
    expect(res.statusCode).toBe(200);
    expect(res.body.token).toBeDefined();
  });

  it('returns 401 for invalid password', async () => {
    const res = await request(app)
      .post('/login')
      .send({ username: 'admin', password: 'wrong' });
    expect(res.statusCode).toBe(401);
  });
});

Real‑world tip: Use a separate test database or seed data to avoid side effects.

Conclusion

These 12 prompts cover the most common testing scenarios: unit, integration, mocking, I/O, and even containerised databases. Adapt them to your stack, keep your tests deterministic and fast. Start with one prompt, paste your code, and watch the tests write themselves.

For more patterns, refer to the official docs: pytest fixtures, Jest mocks, and unittest.mock.

← All posts

Comments