The four levels of API functional testing

1. Status

  • Did it return 200.

  • Catches outages. Passes happily on an empty result where a record should be.

2. Shape

  • Are the expected fields present with the expected types.

  • Catches schema drift. Says nothing about whether values are right.

3. Value

  • Is the total actually the sum, is the status the one this action should produce.

  • Where real defects start getting caught.

The fourth is relationship: after creating an order, does fetching the customer show it. Most interesting bugs live between two calls rather than inside one, and level four is the only one that looks there.

Why suites stop at level two

Because levels three and four need to know what correct means, which means someone has to decide. Status and shape can be derived mechanically from a specification. Value and relationship require a judgement about the product.

That is genuinely harder, and it is also the entire value. A suite that never expresses a product rule is checking that the service is online in an elaborate way.

The negative case people skip

For every endpoint, ask what should be rejected, then assert that it is. Out-of-range values, missing required fields, an identifier belonging to somebody else. Services that return 200 with an error object in the body are common enough that a suite unaware of the pattern will report green forever.

What a suite needs underneath

Four things decide whether an API suite survives its first year: sessions that expire, values that only exist at runtime, calls that depend on each other, and records nobody cleans up. TestSprite handles these as Auto-Authentication, Dynamic Variables, Dependency Chains and Auto-Cleanup, described in the API testing documentation.

Getting these right is what lets you spend your attention on levels three and four instead of on infrastructure.

Getting coverage past the shallow end

Generated plans start from a specification or a discovery pass and include functional, schema, authorization, error-handling and boundary categories, so the negative cases exist by default rather than depending on someone remembering them at the end of a sprint.

Terminal

npm install -g @testsprite/testsprite-cli
testsprite setup

The same setup is available in the TestSprite dashboard if you would rather not install anything locally. The rest of the CLI surface is in the CLI repository.

Choosing which endpoints get level four

Relationship assertions are the most valuable and the most expensive, so they should not be everywhere. The filter that works is to ask what a wrong answer would cost.

Anything that moves money, changes permissions, or is the basis of a decision someone else makes gets level four. An order that exists but is not attached to a customer, a permission grant that succeeded on one side only, a report that is internally consistent and disagrees with the underlying records. These are the failures that are expensive and quiet.

Read-only reporting endpoints, health checks and anything whose output a person eyeballs immediately do not need it. Being deliberate about this is what keeps the suite fast enough that people run it.

Run it on every change

Two routes, and the choice is really about who owns the pipeline. Connecting the GitHub App from the dashboard needs no change to your repository at all, because it reacts to the deployment your build already emits. Adding a GitHub Actions step puts the check in the repo where it gets reviewed like the rest of the build.

How TestSprite gets you past level two

Generated plans include the negative cases by default, across functional, schema, authorization, error-handling and boundary categories, which is where most hand-written suites run out of energy. You refine them in plain language, and that refinement is where your product knowledge enters the suite.

The value and relationship levels are the ones worth your attention, and they are also the ones that need someone who knows the product. Auto-Authentication keeps sessions alive across a run, Dynamic Variables carry values between calls, Dependency Chains derive execution order from what each case needs and produces, and Auto-Cleanup removes exactly what the run created. so the time you spend goes on what correct means rather than on infrastructure.

What you get is a suite that asserts on bodies and on relationships between calls, running on every change, rather than a green pipeline that proves the service is online.

Is status code assertion useless?

No, it is just the floor. Keep it and add at least one value-level assertion per endpoint that matters.

How do we decide what correct means?

Ask what the product promises, not what the code does. If nobody can answer for a given endpoint, that ambiguity is itself worth surfacing.

Should every endpoint get all four levels?

No. Reserve levels three and four for endpoints where a wrong value has consequences. Reporting endpoints and health checks do not need them.

What about generated assertions?

Useful for levels one and two, which are mechanical. Levels three and four benefit from review, because that is where product knowledge enters.

How many negative cases per endpoint?

One or two that matter beats ten that enumerate every possible bad input. Pick the rejections a real client would trigger.

The short version

Status and shape are the floor, not the job.

API functional testing has four levels of assertion and most suites stop at the second. Value and relationship assertions are where defects get caught, and the negative case is the one people skip.