Skip to content

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 , 9 min read

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, 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 with typical values. A user journey test 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. 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 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 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 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.

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.

Where the happy path, edge cases, and corner cases sit Valid input 100 characters 1 character Address length 1 item 20 items Items in cart Happy path 2 items, 12 Elm Street Edge case 100 characters Edge case 20 items Corner case 20 items and 100 characters Negative test: empty address Negative test: 21 items
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 and 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 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:

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:

________ 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 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 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, 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:

AttributeHappy pathEdge caseCorner case
InputsTypical and validOne input at its limitTwo or more inputs at their limits
Acme example2 items and "12 Elm Street"20 items20 items and a 100-character address
What the test checksThe feature reaches its goalThe feature still works at the limitThe limits still work together
How many there areUsually one per featureAt least one per limitGrows fast with each input that has limits
Who usually finds itThe developer, in the first demoA tester reading the rulesInput generators, or customers after release
Why it gets skippedIt rarely doesRequests rarely name limitsEach 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 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 →

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.