How to Run API Tests Without Tavern's YAML Boilerplate

Tavern writes API tests as pytest YAML. See a fair comparison and a no-YAML alternative: build tests against your spec and run them headlessly with the Apidog CLI.

INEZA Felin-Michel

INEZA Felin-Michel

16 June 2026

How to Run API Tests Without Tavern's YAML Boilerplate

Apidog for Enterprise

On-Premises Deploy

SSO & RBAC

SOC 2 Compliant

Explore Apidog Enterprise

Tavern is a smart piece of engineering. It plugs into pytest, lets you describe an API test as a YAML file, and runs that file as if it were a normal pytest test. You get pytest’s whole ecosystem for free: fixtures, plugins, parallel runs with pytest-xdist, coverage, the same command your Python team already uses. For a backend team that lives in pytest, writing one more test_*.tavern.yaml next to the unit tests feels natural.

The friction shows up once the YAML grows. A single request that sends a body, saves a token, and checks a few response fields turns into a nested block of request, response, save, and verify_response_with keys that you have to indent exactly right. Multi-stage flows (log in, create a resource, read it back, delete it) stack those blocks into one long file where a misplaced space breaks the parse and the API contract the test verifies lives somewhere else entirely: a Swagger file, a wiki page, a Postman collection that drifted out of date months ago.

button

What Tavern does well

Start with the credit, because Tavern earns it.

It rides on pytest instead of replacing it. Tavern is a pytest plugin. You install it with pip install tavern, drop a YAML file in your test directory, and pytest discovers and runs it like any other test. That means you keep every pytest habit your team already has: -k to filter, -x to stop on first failure, pytest-html for reports, pytest-xdist for parallel runs, fixtures for shared setup. Nothing about your runner changes. For a Python shop, that is a real advantage, and few API test tools integrate that cleanly with an existing suite.

The YAML is declarative and readable. A simple Tavern test is genuinely easy to scan:

test_name: Get a single order

stages:
  - name: Fetch order 1042
    request:
      url: https://api.shop.test/orders/1042
      method: GET
    response:
      status_code: 200
      json:
        id: 1042
        status: shipped

No glue code, no assertion library to import, no test harness to write. The request and the expected response sit side by side. For checking a status code and a couple of fields, that reads better than the equivalent requests plus assert Python.

Variable saving and chaining. Tavern can pull a value out of one response and inject it into the next stage with save and {variable} substitution, so a login-then-call flow works without custom code:

stages:
  - name: Log in
    request:
      url: https://api.shop.test/auth/login
      method: POST
      json:
        username: tester
        password: hunter2
    response:
      status_code: 200
      save:
        json:
          token: access_token

  - name: Read the profile with the saved token
    request:
      url: https://api.shop.test/me
      method: GET
      headers:
        Authorization: "Bearer {token}"
    response:
      status_code: 200

It does more than HTTP. Tavern also tests MQTT, which matters if you work with IoT or message-driven systems. And because it is pytest underneath, you can mix YAML API tests and regular Python tests in the same run and the same report.

None of that is marketing. Tavern is a solid tool. The question is whether its assumptions match yours.

Where the YAML boilerplate piles up

Three costs come bundled with the Tavern model, and whether they matter depends on how big your suite gets.

YAML is whitespace-significant, and the schema is deep. The simple example above is clean. A realistic test is not. Once you add headers, query parameters, a request body, response-body assertions, type checks, saved variables, and verify_response_with external functions, you are nesting four and five levels deep in a format where one wrong space is a parse error, not a clear message. Editors do not autocomplete Tavern’s keys the way an IDE autocompletes a Python method, so you are checking the docs for whether it is status_code or status, json or body, on each new field.

The test and the API contract are two separate artifacts. Your Tavern YAML hardcodes the URL, the method, the expected fields, and their types. The actual API definition lives in an OpenAPI file, a framework’s route decorators, or nobody’s-quite-sure-where. When the API changes a field name, nothing tells the YAML. The test keeps asserting the old shape until it fails in CI or, worse, keeps passing against a stale expectation. Someone has to keep the two in sync by hand, and that someone is usually whoever the failure lands on at 2 a.m.

It assumes a Python toolchain and Python people. Tavern is great if your testers write Python. If your QA group, your frontend engineers, or your product folks want to add or read a test, they hit pip, virtualenvs, pytest conventions, and YAML schema rules all at once just to send an HTTP request and check a response. The on-ramp is steep for anyone outside the Python world.

The skip-the-YAML path

The alternative is to stop authoring tests as text files and start building them against the API definition you already have.

In Apidog, the API spec, the request, and the test are the same object. You import or design your API once (OpenAPI, Swagger, or a Postman collection imports in a click), and each endpoint carries its real schema with it. You build a test scenario by chaining those endpoints visually: drag in the login call, drag in the call that needs the token, and pass the value forward without writing save: blocks or {variable} strings by hand. Assertions are checkboxes and expressions in a panel, not indented YAML keys you have to remember.

Because the test is built against the spec, the spec is the single source of truth. Change a response field in the design and the test scenarios that reference it surface the change instead of silently drifting. That is the part Tavern structurally cannot do: its YAML has no link back to the contract.

And when it is time to automate, you do not lose the headless, CI-friendly property that made Tavern attractive. The Apidog CLI runs those same scenarios from the command line, in CI, with no GUI, exactly the way pytest runs your Tavern files today.

Installing and running the Apidog CLI

The runner is a free npm package called apidog-cli. Install it globally:

npm install -g apidog-cli

Then run a test scenario with the apidog run command:

apidog run --access-token $APIDOG_ACCESS_TOKEN -t 605067 -e 1629989 -r cli

Here is what each piece does:

You do not have to memorize those IDs. Open the test scenario in Apidog, switch to its CI/CD tab, choose the command-line option, and click Generate token. Apidog builds the full apidog run command for you with the scenario ID and environment ID already filled in. Copy it, then move the token into a CI secret.

If you would rather not install globally, especially on an ephemeral CI runner, run it with npx:

npx apidog-cli run --access-token $APIDOG_ACCESS_TOKEN -t 605067 -e 1629989 -r cli

The reporters cover cli, html, json, and junit. For CI, the useful combination is -r html,junit: junit emits the standard XML that almost every CI dashboard parses into a pass/fail tree, and html produces a browsable report you can archive as a build artifact. Add --out-dir to control where they land:

apidog run --access-token $APIDOG_ACCESS_TOKEN -t 605067 -e 1629989 -r html,junit --out-dir ./apidog-reports

For the full flag list on your installed version, run apidog run --help.

Side by side

Tavern Apidog
Test format YAML files (*.tavern.yaml) Visual scenarios built against the spec
Runtime Python + pytest Headless apidog run (npm package)
Authoring Hand-write and indent YAML Drag-and-chain endpoints, checkbox assertions
API contract Separate artifact, kept in sync by hand Spec is the test’s source of truth
Variable chaining save: blocks and {var} substitution Pass values between steps in the UI
Reporters pytest-html, JUnit via pytest plugins cli, html, json, junit built in
Who can write tests Python-comfortable testers Anyone on the team, including non-coders
Protocols HTTP and MQTT HTTP, plus SOAP, WebSocket, gRPC, and more

Tavern wins if your team is all-in on Python and you want tests living in the same repo and the same pytest run as your unit tests. Apidog wins if you want to drop the YAML maintenance, keep the contract and the test as one thing, and let people who do not write Python contribute tests.

Wiring it into CI

The headless run is the whole point of moving from a local test file to a pipeline. Here is the GitHub Actions shape:

name: API tests
on: [push, pull_request]

jobs:
  api-tests:
    runs-on: ubuntu-latest
    steps:
      - name: Install Apidog CLI
        run: npm install -g apidog-cli

      - name: Run API test scenario
        run: |
          apidog run \
            --access-token "$APIDOG_ACCESS_TOKEN" \
            -t 605067 \
            -e 1629989 \
            -r html,junit \
            --out-dir apidog-reports
        env:
          APIDOG_ACCESS_TOKEN: ${{ secrets.APIDOG_ACCESS_TOKEN }}

      - name: Upload report
        if: always()
        uses: actions/upload-artifact@v4
        with:
          name: apidog-report
          path: apidog-reports

The token comes from repository secrets and reaches the step as an environment variable. The if: always() on the upload step means you still get the report when the tests fail, which is exactly when you want to read it. If an assertion fails, the apidog run step exits non-zero, the job goes red, and the pull request shows a failing check.

That exit-code behavior is the quality gate, and it works the same way Tavern’s does: CI reads the exit code of each step, a non-zero exit fails the job, and a failed job blocks the merge or the deploy. You do not configure anything extra. For deeper pipeline setups, Apidog CLI in your CI/CD pipeline and the GitHub Actions walkthrough cover GitLab CI and Jenkins variants too.

A note on pytest and the wider Python testing world

Moving off Tavern’s YAML does not mean abandoning pytest. Plenty of teams keep their pure-Python unit and integration tests in pytest and use Apidog for the API-contract layer, the part where the test should track the spec and run for non-Python contributors too. The two are not mutually exclusive. Tavern’s value was always letting pytest people write API tests in a lighter format than raw requests code; Apidog’s value is making those API tests track the contract and stay readable as the suite grows past a handful of files.

If you are also weighing other runners, the same logic applies to Postman’s command-line story in Postman CLI vs Newman and to running collections without extra tooling in API testing without Postman.

FAQ

Is the Apidog CLI free? Yes. The apidog-cli npm package is free to install and run with npm install -g apidog-cli. It executes the test scenarios from your Apidog project, so what you can run depends on your Apidog plan, but the command-line runner itself is not a separate paid product.

Can I still use pytest alongside Apidog? Yes. Keep your Python unit and integration tests in pytest and use Apidog for the API-contract test layer. Many teams run both. The difference is that Apidog tests track the spec instead of hardcoding it in a separate file.

How does Apidog block a bad merge in CI? Through the exit code. When any assertion fails, apidog run exits non-zero. CI reads that exit code, marks the step as failed, and blocks the merge or deploy. You do not configure anything extra for this to work.

Which reporter should I use for CI? Use junit for the machine-readable result your CI dashboard parses into a pass/fail tree, and add html if you want a browsable report saved as an artifact. A common choice is -r html,junit, with cli kept on for readable build-log output.

Does Apidog only test HTTP, like Tavern’s HTTP mode? No. Apidog covers HTTP plus SOAP, WebSocket, Server-Sent Events, and gRPC. Tavern adds MQTT to HTTP, which is the one place its protocol coverage goes somewhere Apidog’s does not, so keep that in mind if MQTT is central to your stack.

The honest takeaway

Tavern is a clean way to write API tests if you already think in pytest and YAML, and the pytest integration is genuinely its best feature. The cost is the YAML itself: it grows deep and brittle as suites scale, and it has no link back to the API contract it verifies. If you want the same headless, fail-loud-in-CI behavior without hand-indenting YAML and without maintaining a second copy of your spec, building tests against the contract and running them with apidog run is the lighter path.

Download Apidog and import an existing API to feel the spec-is-the-test workflow before you commit. It is free to start.

Explore more

How to Add Test Automation to Your DevOps Pipeline

How to Add Test Automation to Your DevOps Pipeline

Test automation in DevOps maps API tests across the lifecycle: PR gates, integration checks, deploy smoke tests, and monitoring, wired up with the Apidog CLI.

16 June 2026

How to Diff OpenAPI Specs and Block Breaking Changes in CI

How to Diff OpenAPI Specs and Block Breaking Changes in CI

Diff two OpenAPI spec versions to catch breaking API changes before merge. Use oasdiff or openapi-diff in CI, then close the contract gap with the Apidog CLI.

16 June 2026

Apidog CLI vs Redocly CLI: which API CLI should you use?

Apidog CLI vs Redocly CLI: which API CLI should you use?

Apidog CLI vs Redocly CLI compared command by command: lint, bundle, split, build docs, run tests, and mock. An honest verdict on which API CLI fits your team.

16 June 2026

Practice API Design-first in Apidog

Discover an easier way to build and use APIs

How to Run API Tests Without Tavern's YAML Boilerplate