---
name: writing-unit-tests
description: A comprehensive guide on how to write effective unit tests for software development projects, covering various programming languages and testing frameworks.
license: MIT
compatibility: Python (unittest, pytest), JavaScript (Jest, Mocha), Java (JUnit), C# (NUnit)
---

# Writing Unit Tests

> Ensure code reliability and maintainability by systematically verifying individual components.

## Why this skill exists
Writing unit tests is crucial for maintaining high-quality software. Without proper testing, bugs can slip through the cracks, leading to unexpected behavior in production environments. Unit tests help isolate and verify the functionality of individual units of code, ensuring they work as intended under various conditions.

## When to trigger

| Trigger | Example |
|---------|---------|
| User says "I need to write unit tests for my function." | "Can you show me how to write unit tests for this function?" |
| Task implies writing new features | "We're adding a new feature, so we should also add unit tests." |
| Code changes are made | "After refactoring the code, I want to update the unit tests accordingly." |
| Before deploying code to production | "Before deploying, let's run all the unit tests to ensure everything works correctly." |
| User says "I need to debug a failing test" | "The unit test is failing; can you help me figure out why?" |
| Task implies fixing bugs | "We found a bug in the application, so we should add a unit test to prevent it from happening again." |
| Code review process starts | "As part of the code review, let's ensure all necessary unit tests are present." |
| User says "I want to improve test coverage" | "How can I increase the test coverage for this module?" |

| Do NOT trigger when | Example |
|---------------------|---------|
| User asks about integration or end-to-end testing | "Should we write unit tests for this API endpoint?" |
| Task involves non-code activities | "I need to update the project documentation." |
| User requests performance testing | "How can I test the performance of this function?" |
| Code is not yet written | "Before writing the code, let's discuss how we'll test it." |

## Quick-reference cheatsheet

| Situation | Action | Notes |
|-----------|--------|-------|
| New feature development | Write unit tests for each new function or method. | Ensure tests cover all edge cases and expected behaviors. |
| Code refactoring | Update existing unit tests to reflect changes in the codebase. | Verify that tests still pass after refactoring. |
| Bug fixing | Add a unit test that reproduces the bug before fixing it. | This helps prevent regressions. |
| Before deployment | Run all unit tests and ensure they pass. | Use continuous integration tools for automated testing. |
| Code review | Check for comprehensive unit tests covering critical paths. | Suggest improvements if necessary. |

## Core steps

1. **Understand the Functionality**
   - Analyze the code to understand its inputs, outputs, and expected behavior.
   - Identify edge cases and potential failure points.

2. **Choose a Testing Framework**
   - Select an appropriate testing framework based on the programming language (e.g., `unittest` for Python, `Jest` for JavaScript).
   - Install the chosen framework if it's not already available in your environment.

3. **Set Up the Test Environment**
   - Create a separate directory for test files.
   - Organize tests logically, grouping them by functionality or module.

4. **Write Test Cases**
   - For each function or method, write at least one test case covering its primary functionality.
   - Use assertions to verify that the actual output matches the expected output.
   - Include test cases for edge cases and error handling scenarios.

5. **Run Tests Locally**
   - Execute the tests using the testing framework's command-line interface.
   - Review the results to identify any failing tests or issues.

6. **Debug Failing Tests**
   - Analyze the failure messages to understand why a test is failing.
   - Modify the code or test as necessary to resolve the issue.
   - Re-run the tests to ensure they pass.

7. **Integrate with Continuous Integration (CI)**
   - Configure your CI pipeline to automatically run unit tests on code changes.
   - Set up alerts for test failures to notify the development team promptly.

8. **Maintain and Update Tests**
   - Regularly update tests to reflect changes in the codebase.
   - Remove or refactor obsolete tests as needed.
   - Ensure that test coverage remains high and relevant.

## Common patterns

### Pattern 1: Testing a Calculator Function

**Scenario:** You have a simple calculator function that performs addition, subtraction, multiplication, and division.

**Input:**
```python
def calculate(a, b, operation):
    if operation == 'add':
        return a + b
    elif operation == 'subtract':
        return a - b
    elif operation == 'multiply':
        return a * b
    elif operation == 'divide':
        if b == 0:
            raise ValueError("Cannot divide by zero")
        return a / b
```

**Reasoning:** The function has multiple operations and needs to handle edge cases like division by zero.

**Output:**
```python
import unittest

class TestCalculator(unittest.TestCase):
    def test_addition(self):
        self.assertEqual(calculate(2, 3, 'add'), 5)

    def test_subtraction(self):
        self.assertEqual(calculate(5, 3, 'subtract'), 2)

    def test_multiplication(self):
        self.assertEqual(calculate(4, 6, 'multiply'), 24)

    def test_division(self):
        self.assertEqual(calculate(10, 2, 'divide'), 5)
        with self.assertRaises(ValueError):
            calculate(10, 0, 'divide')
```

### Pattern 2: Testing an API Endpoint

**Scenario:** You have a RESTful API endpoint that returns user data.

**Input:**
```javascript
const express = require('express');
const app = express();

app.get('/users/:id', (req, res) => {
    const userId = req.params.id;
    // Fetch user from database
    const user = { id: userId, name: 'John Doe' };
    res.json(user);
});

module.exports = app;
```

**Reasoning:** The endpoint needs to handle different user IDs and return the correct data.

**Output:**
```javascript
const request = require('supertest');
const app = require('./app');

describe('GET /users/:id', () => {
  it('should return a user by ID', async () => {
    const response = await request(app).get('/users/123');
    expect(response.status).toBe(200);
    expect(response.body).toEqual({ id: '123', name: 'John Doe' });
  });

  it('should handle invalid user IDs gracefully', async () => {
    const response = await request(app).get('/users/invalid-id');
    expect(response.status).toBe(404);
    expect(response.body.message).toBe('User not found');
  });
});
```

### Pattern 3: Testing a Database Query

**Scenario:** You have a function that queries a database to retrieve user information.

**Input:**
```java
public class UserRepository {
    public User getUserById(int id) {
        // Simulate database query
        if (id == 1) {
            return new User(1, "Alice");
        } else {
            throw new UserNotFoundException("User not found");
        }
    }
}
```

**Reasoning:** The function needs to handle both successful queries and exceptions.

**Output:**
```java
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;

public class UserRepositoryTest {
    @Test
    public void testGetUserById_success() {
        UserRepository repo = new UserRepository();
        User user = repo.getUserById(1);
        assertNotNull(user);
        assertEquals("Alice", user.getName());
    }

    @Test
    public void testGetUserById_failure() {
        UserRepository repo = new UserRepository();
        assertThrows(UserNotFoundException.class, () -> {
            repo.getUserById(2);
        });
    }
}
```

## Edge cases & gotchas

| Edge Case | What goes wrong | How to handle it |
|-----------|-----------------|------------------|
| Unhandled exceptions | Tests fail due to unexpected errors. | Use try-catch blocks or assertions to handle exceptions gracefully. |
| Insufficient test coverage | Critical paths are not tested, leading to undetected bugs. | Review code and add tests for all important scenarios. |
| Mocking issues | External dependencies cause tests to fail. | Use mocking frameworks (e.g., `unittest.mock` in Python) to isolate the unit being tested. |
| Test data setup/teardown | Tests rely on external state, causing flakiness. | Use fixtures or setup/teardown methods to manage test data consistently. |
| Performance bottlenecks | Slow tests impact development workflow. | Optimize tests by focusing on critical paths and avoiding unnecessary computations. |

## Decision tree

  Is the code new?
  ├─ Yes → Write unit tests for each function/module.
  └─ No →
        Is the code refactored?
        ├─ Yes → Update existing tests to reflect changes.
        └─ No →
              Are there known bugs?
              ├─ Yes → Add tests that reproduce and fix the bug.
              └─ No → Ensure comprehensive test coverage for critical paths.

## Do NOT use this skill for

- **Integration or end-to-end testing:** Use separate tools like Selenium or Postman for these types of tests.
- **Non-code activities:** This skill is focused on writing unit tests for code, not other development tasks.
- **Performance testing:** Use profiling tools and benchmarks instead of unit tests for performance analysis.
- **Code that is not yet written:** Ensure you have the code before writing tests to avoid premature optimization.
- **Testing external systems:** Focus on testing your own codebase; use mocks or stubs for external dependencies.

## References & further reading

| Resource | What it covers | URL |
|----------|----------------|-----|
| "The Art of Unit Testing" by Roy Osherove | Comprehensive guide to unit testing principles and practices. | [Link](https://www.amazon.com/Art-Unit-Testing-Roy-Osherove/dp/0134675852) |
| "JUnit 5 User Guide" | Official documentation for JUnit, a popular Java testing framework. | [Link](https://junit.org/junit5/docs/current/user-guide/) |
| "Testing JavaScript Applications" by Azer Koçulu | Detailed guide to testing JavaScript applications using various frameworks. | [Link](https://www.amazon.com/Testing-JavaScript-Applications-Azer-Koçulu/dp/149203062X) |
| "Python Testing with pytest" by Brian Okken | Comprehensive guide to using pytest for Python testing. | [Link](https://pragprog.com/titles/bkpyt3/python-testing-with-pytest/) |
| "Effective Unit Testing" by Robert C. Martin | Best practices and strategies for writing effective unit tests. | [Link](https://www.amazon.com/Effective-Unit-Testing-Robert-C-Martin/dp/0134675852) |

---

This SKILL.md provides a detailed guide on how to write unit tests, covering various programming languages and testing frameworks. It includes triggers for when to use the skill, a quick-reference cheatsheet, core steps for writing effective tests, common patterns, edge cases, a decision tree, anti-triggers, and references for further reading.
