QA Test Lab • Runbook 09
← Back to Runbooks Sprint Plan

Runbook 09 — Cypress API harness (setup + first 3 tests)

Quick links

Scope

Rule

If CI can’t run it by env contract, it doesn’t count as “working.”

Exit criteria

Env contract

API_BASE_URL Base URL to the API host. Examples:
  • http://sut.testlab:3001
  • http://127.0.0.1:3001 (NAT/port-forward mode only)
Rule

Do not bake /api into the base URL if your callers already include it. Pick one convention and document it.

API_EMAIL RW user email for login/token acquisition (if POST requires auth).
API_PASSWORD RW user password for login/token acquisition.
API_TOKEN Optional. If supplied, tests may use it instead of logging in. Prefer token acquisition for repeatability unless tokens are stable by design.
Security

Never commit credentials or tokens. Use env vars locally and CI secret variables for pipelines.

Setup

Install

npm install --save-dev cypress

Initialize Cypress folder structure

npx cypress open
Note

This creates cypress/ folders and a baseline config. We keep this repo API-first in Sprint 09.

Config (env-driven base URL)

Starter tests

Convention

Tests validate structure + meaning. Avoid “status code only” tests.

File: cypress/e2e/api/articles.cy.js

npm install --save-dev cypress
npx cypress open
// cypress.config.js
const { defineConfig } = require('cypress')

module.exports = defineConfig({
  e2e: {
    baseUrl: process.env.API_BASE_URL,
    setupNodeEvents(on, config) {
      if (!config.baseUrl) {
        throw new Error('Missing required env: API_BASE_URL')
      }
      return config
    },
  },
})
describe('API.ARTICLES.001 - Get Articles', () => {
  it('returns list with expected shape', () => {
    cy.request('GET', '/api/articles').then((res) => {
      expect(res.status).to.eq(200)
      expect(res.body).to.have.property('articles')
      expect(res.body.articles).to.be.an('array')

      if (res.body.articles.length > 0) {
        const a = res.body.articles[0]
        expect(a).to.have.property('title')
        expect(a).to.have.property('slug')
        expect(a).to.have.property('author')
      }
    })
  })
})

describe('API.ARTICLES.002 - Create Article', () => {
  it('creates a unique article (requires auth)', () => {
    const title = `Sprint09 ${Date.now()}`
    const token = Cypress.env('API_TOKEN')

    // If your API requires login, set API_TOKEN via env or add a login helper in Sprint 10.
    expect(token, 'API_TOKEN is required for create').to.be.a('string').and.not.be.empty

    cy.request({
      method: 'POST',
      url: '/api/articles',
      headers: { Authorization: `Token ${token}` },
      body: {
        article: {
          title,
          description: 'Cypress API harness seed',
          body: 'Sprint 09 POST test'
        }
      }
    }).then((res) => {
      expect(res.status).to.eq(201)
      expect(res.body).to.have.property('article')
      expect(res.body.article.title).to.eq(title)
      expect(res.body.article).to.have.property('slug')
    })
  })
})

describe('API.ARTICLES.003 - Unauthorized Create', () => {
  it('fails with 401 when no token provided', () => {
    cy.request({
      method: 'POST',
      url: '/api/articles',
      failOnStatusCode: false,
      body: {
        article: {
          title: `ShouldFail ${Date.now()}`,
          description: 'no auth',
          body: 'negative path'
        }
      }
    }).then((res) => {
      expect(res.status).to.eq(401)
    })
  })
})
export API_BASE_URL="http://sut.testlab:3001"
curl -sS "$API_BASE_URL/api/articles" | head
export API_BASE_URL="http://sut.testlab:3001"
export API_TOKEN="<token>"   # store in shell/CI secrets, never commit
npx cypress run
Sprint boundary

Sprint 09 allows using API_TOKEN directly. Sprint 10 adds deterministic token acquisition (login) + request helpers.

Commands

Preflight: verify API reachable

export API_BASE_URL="http://sut.testlab:3001"
curl -sS "$API_BASE_URL/api/articles" | head

Run Cypress headless

export API_BASE_URL="http://sut.testlab:3001"
export API_TOKEN="<token>"   # store in shell/CI secrets, never commit
npx cypress run
Note

If you are in NAT/port-forward mode, base URL may be http://127.0.0.1:3001 instead.

Expected outputs

Failure modes

Rule

This sprint proves the harness. Sprint 10 hardens auth + helpers. Sprint 11 turns it into a CI-grade signal.