# What is the difference between a happy path and an edge case?

The happy path is the expected route through software, an edge case sits at the limit of one input, and a corner case combines several limits at once.

Last updated September 29, 2026, 9 min read

## Learning objectives

After reading this article you will be able to:

-   Define the happy path, edge cases, and corner cases
-   Explain when to test beyond the happy path
-   Identify why agent-written tests favor the happy path

## Related content

-   [What is software testing?](https://specstory.com/learning/testing/software-testing)
-   [What is exploratory testing, and can an AI agent do it?](https://specstory.com/learning/testing/exploratory-testing)
-   [What is a user journey test?](https://specstory.com/learning/testing/user-journey-testing)
-   [What is unit testing?](https://specstory.com/learning/testing/unit-testing)

## What is the difference between a happy path and an edge case?

The happy path is a feature's expected route, with valid input and no errors, while an edge case pushes one input to its limit. A corner case pushes two or more inputs to their limits at the same time. Happy path tests check typical use. Edge and corner case tests check the limits.

In [software testing](https://specstory.com/learning/testing/software-testing), teams usually test the happy path first, because a feature that fails there does not work for anyone. A suite with only happy path tests can pass while the software fails on the first unusual input.

The words are used loosely. Many developers say "edge case" for any rare situation and "corner case" for a rarer one. The opposite of the happy path is the unhappy path, which ends in an error or stops early. An edge case with valid input is not an unhappy path, because the feature should still reach its goal.

## What is the happy path?

The happy path is the route a user takes through a feature when the input is valid and nothing goes wrong. It is also called the happy flow or the sunny day scenario. Most demos follow it, and so do many generated tests.

A happy path test is often a [unit test](https://specstory.com/learning/testing/unit-testing) with typical values. A [user journey test](https://specstory.com/learning/testing/user-journey-testing) follows the happy path of one task across several screens.

The unhappy path, also called the sad path, is any route that ends in an error or stops before the goal, e.g. a declined card. A command-line tool has unhappy paths too, e.g. a run that a user stops halfway with [Ctrl-C](https://specstory.com/learning/cli-and-web/sigint-graceful-shutdown). A passing happy path test says nothing about those routes.

## What is an edge case?

An edge case is one input at the end of its allowed range, e.g. a full cart. The International Software Testing Qualifications Board (ISTQB) [defines an edge case](https://glossary.istqb.org/en_US/term/edge-case) as a test condition inside a system's limits that is uncommon, extreme, or atypical. That wider definition also covers unusual input with no numeric limit, e.g. an address in another alphabet.

A boundary value is the lowest or highest value of a range, e.g. 20 items in a cart that allows 20. Boundary values inside the allowed range are a common kind of edge case. [Boundary value analysis](https://specstory.com/learning/testing/boundary-value-analysis) is the technique that picks them, along with the first values outside the range.

Negative testing is a testing technique that checks how software handles invalid, unexpected, or missing input. ISTQB [defines negative testing](https://glossary.istqb.org/en_US/term/negative-testing) as testing in which software is used in a way it is not intended. An edge case stays inside the limits, and a negative test goes past them, e.g. 21 items. Negative testing is one form of [adversarial testing](https://specstory.com/learning/test-quality/adversarial-testing).

## What is a corner case?

A corner case is a situation in which two or more inputs sit at their limits at the same time. It is also called a pathological case. The names come from geometry. When two inputs are drawn on two axes, their valid values form a rectangle. One input at its limit lies on an edge, and both at once lie in a corner.

Diagram: Where the happy path, edge cases, and corner cases sit

An edge case moves one input to its limit, and a corner case moves two at once. A negative test goes past a limit.

Corner cases are hard to find, because each limit passes its own edge case test and the failure appears only in the combination. They also multiply. Three inputs with a low and a high limit each have 8 corners, and each added input doubles the count.

A practical start is to pair inputs that the software stores together, e.g. the cart and the address in one checkout session. [Property-based testing](https://specstory.com/learning/test-quality/property-based-testing) and [fuzzing](https://specstory.com/learning/test-quality/fuzzing) generate combinations that nobody listed.

## When should a team test beyond the happy path?

Here is an illustrative example. Acme Co. sells furniture online, with at most 20 items per cart and 100 characters per address. A developer asks a [coding agent](https://specstory.com/learning/ai-coding/coding-agent) to "Let customers edit their delivery address during checkout." The agent's test covers only the happy path, so a tester adds a parametrized pytest test:

```python
import pytest
from checkout import start_checkout
LONG = "12 Elm Street " + "x" * 86  # 100 characters, the field's limit

@pytest.mark.parametrize("items, address", [
    pytest.param(2, "12 Elm Street", id="happy-path"),
    pytest.param(20, "12 Elm Street", id="edge-full-cart"),
    pytest.param(2, LONG, id="edge-longest-address"),
    pytest.param(20, LONG, id="corner-full-cart-longest-address"),
])
def test_address_edit_keeps_cart(items, address):
    order = start_checkout(items=items)
    order.set_address(address)
    assert order.address == address
    assert len(order.items) == items
```

Each case runs as its own test, reported by its id. The happy path and both edge cases pass, and the corner case fails:

```text
________ test_address_edit_keeps_cart[corner-full-cart-longest-address] ________
>       assert len(order.items) == items
E       assert 0 == 20
E        +  where 0 = len([])
E        +    where [] = <checkout.Order object at 0x1054d0e90>.items
```

Together, the full cart and the longest address overflowed Acme's checkout session, and the session code dropped the cart. The address saved, but the cart emptied. Two negative tests, an empty address and one of 101 characters, expect a rejection with the cart intact.

A prototype that only its builder uses can often stop at the happy path. Other software needs more tests in three places:

-   **Outside input.** Input from users or other programs can arrive empty or malformed, so it needs negative tests.
-   **Stated limits.** Each limit gets a test at the limit and one a step past it.
-   **Money and customer data.** A checkout needs its edge cases, and a [two-account access test](https://specstory.com/learning/cli-and-web/two-account-access-test) checks that customers cannot open each other's orders.

This example is simplified. A real checkout would also need edge cases for payment, e.g. a total of $0.00.

## What changes when a coding agent writes the code?

A coding agent writes code and tests from its prompt, and a prompt usually describes the happy path. Acme's request says nothing about a full cart or an empty field, so the agent's test sends typical input and passes. This is one reason [AI-written tests](https://specstory.com/learning/verification/ai-written-tests-always-pass) can pass on broken code.

An agent asked for edge case tests after the code exists often derives them from the code. It then tests the limits the code already checks, e.g. the 100-character rule on the address field. A combination the code does not handle has no branch to read, so it gets no test. Acme's code has no rule for the cart size and the address length together, so edge case tests derived from that code would not combine them.

A practical adjustment starts before the agent does. The person who asked for the change lists the limits and invalid inputs as [acceptance criteria](https://specstory.com/learning/ai-coding/acceptance-criteria), or a tester does. The list also names the corners, e.g. a full cart with the longest address. The list becomes tests that the agent does not edit.

## How do the happy path, edge cases, and corner cases compare?

The three kinds of case differ on these attributes:

| Attribute | Happy path | Edge case | Corner case |
| --- | --- | --- | --- |
| Inputs | Typical and valid | One input at its limit | Two or more inputs at their limits |
| Acme example | 2 items and "12 Elm Street" | 20 items | 20 items and a 100-character address |
| What the test checks | The feature reaches its goal | The feature still works at the limit | The limits still work together |
| How many there are | Usually one per feature | At least one per limit | Grows fast with each input that has limits |
| Who usually finds it | The developer, in the first demo | A tester reading the rules | Input generators, or customers after release |
| Why it gets skipped | It rarely does | Requests rarely name limits | Each limit passes its own test |

## How do you test the paths nobody wrote down?

A list from the request covers only the limits that someone named. Other paths can show up when someone uses the running software without a script, e.g. in a short [exploratory testing](https://specstory.com/learning/testing/exploratory-testing) session.

RunStory runs your software in a separate environment, tries relevant workflows, and checks the results. You keep working. It is in private alpha for CLIs and web apps.

[Join the RunStory alpha →](https://specstory.com/runstory#alpha)

## FAQs

### What is negative testing?

Negative testing sends software input that it should refuse, e.g. an empty address field, and checks the response. A negative test passes when the software rejects the input and keeps its data intact. Edge case tests stay inside the limits, and negative tests go past them.

### Are edge cases the same as boundary values?

Edge cases and boundary values overlap, but they are not the same. A boundary value is the lowest or highest value of a range, e.g. 20 items in a cart, and one inside the allowed range is a kind of edge case. Edge cases also include unusual input with no numeric limit, e.g. an address in another alphabet.

### How many edge cases should a test cover?

A test should usually cover one edge case, so that a failure names the case that broke. A parametrized test runs the same steps once per case and reports each case by its id. Each stated limit needs a test at the limit and a negative test one step past it.

### Who should list the edge cases for a feature?

The edge cases for a feature are best listed from the request, before the code exists, by the person who asked for the feature or a tester. A list written from the finished code usually covers the limits the code already checks and misses the combinations it does not handle.

---

Source: [Happy path vs. edge case vs. corner case | SpecStory](https://specstory.com/learning/testing/happy-path-and-edge-cases)
