Skip to content
End To End Tester

Testing in Bitbucket Pipelines

The simplest CI model of the five — steps, caches, services and parallel groups — with the hard limits you need to design around.

2 min read · updated 19 September 2026

Bitbucket Pipelines is the simplest of the five platforms here: one YAML file, a container per step, and very little configuration surface. That simplicity is real and so are the limits.

#A complete pipeline

yaml
# bitbucket-pipelines.yml
image: node:22

definitions:
  caches:
    playwright: ~/.cache/ms-playwright

  services:
    postgres:
      image: postgres:16-alpine
      variables:
        POSTGRES_DB: test
        POSTGRES_PASSWORD: test

  steps:
    - step: &lint
        name: Lint and typecheck
        caches: [node]
        script:
          - npm ci
          - npm run lint
          - npm run typecheck

    - step: &unit
        name: Unit tests
        caches: [node]
        script:
          - npm ci
          # Bitbucket picks up JUnit XML from test-results/ automatically.
          - npm test -- --coverage --reporters=default --reporters=jest-junit
        artifacts:
          - coverage/**
        after-script:
          - echo "exit code $BITBUCKET_EXIT_CODE"

    - step: &integration
        name: Integration tests
        caches: [node]
        services: [postgres]
        script:
          - npm ci
          - npm run test:integration
        # DATABASE_URL points at localhost: services share the step's network.

pipelines:
  pull-requests:
    '**':
      - step: *lint
      - parallel:
          - step: *unit
          - step: *integration
      - parallel:
          fail-fast: false
          steps:
            - step: &e2e
                name: E2E 1/3
                size: 2x                 # 8GB — browsers need it
                caches: [node, playwright]
                script:
                  - npm ci
                  - npx playwright install --with-deps chromium
                  - npm run build
                  - npx playwright test --shard=1/3 --reporter=junit
                artifacts:
                  - playwright-report/**
                  - test-results/**
            - step:
                <<: *e2e
                name: E2E 2/3
                script:
                  - npm ci
                  - npx playwright install --with-deps chromium
                  - npm run build
                  - npx playwright test --shard=2/3 --reporter=junit
            - step:
                <<: *e2e
                name: E2E 3/3
                script:
                  - npm ci
                  - npx playwright install --with-deps chromium
                  - npm run build
                  - npx playwright test --shard=3/3 --reporter=junit

  branches:
    main:
      - step: *lint
      - parallel:
          - step: *unit
          - step: *integration
      - step:
          name: Deploy
          deployment: production
          trigger: manual
          script:
            - ./deploy.sh

YAML anchors (&name / *name) are how you avoid repeating steps. Bitbucket has no template mechanism beyond them, so a long pipeline is genuinely repetitive — this is the platform's main scaling weakness.

#Test reports

There is nothing to configure. Write JUnit XML into test-results/ and the results appear on the pipeline page with failures linked.

javascript
// jest.config.js
reporters: [
  'default',
  ['jest-junit', { outputDirectory: 'test-results', outputName: 'junit.xml' }]
]
typescript
// playwright.config.ts
reporter: [['junit', { outputFile: 'test-results/junit.xml' }], ['html']]

#The limits to design around

Limit
Step duration 120 minutes
Memory 4GB default, 8GB (size: 2x), 16GB (4x) on larger plans
Services memory shared with the step's total
Artefacts 1GB per step, kept 14 days
Concurrent steps plan-dependent

size: 2x for anything involving a browser. Chromium plus the application plus Node in 4GB is tight, and the failure mode is an out-of-memory kill that looks like a flaky test.

Each step is a fresh container. Anything one step produces that another needs must be an artefact:

yaml
- step:
    name: Build
    script: [npm ci, npm run build]
    artifacts: ['dist/**', '.next/**']
- step:
    name: Test the build
    script: [npm ci, npm run test:built]     # dist/ was restored automatically

Build minutes are metered. A wasteful matrix shows up on the bill immediately, which is an unusually direct feedback loop. It is a good reason to run the full browser suite on main and a subset on pull requests.

#Caching

yaml
definitions:
  caches:
    playwright: ~/.cache/ms-playwright
    gradle: ~/.gradle/caches

Bitbucket's caches are keyed by name and invalidated on a heuristic rather than by an explicit key, which is less precise than GitHub Actions. For the Playwright browser download — several hundred megabytes — it is still very much worth having.

#Where it fits

Bitbucket Pipelines suits a team already on Bitbucket with a suite that comfortably fits the step limits. It is genuinely the easiest of the five to get productive with.

It scales less well: no templates, limited caching control, no built-in test analytics, and a repetitive file once the pipeline grows. At that point, teams tend to move to GitHub Actions or self-hosted Jenkins.

Common questions

What are the main limits of Bitbucket Pipelines?
A step is capped at 120 minutes, the default container has 4GB of memory (8GB on larger sizes), and each step gets a fresh container. Build minutes are metered per plan, which makes wasteful matrices expensive in a way that is immediately visible.
How do I run tests in parallel in Bitbucket?
A parallel block runs its steps concurrently on separate agents. Each step must slice the suite itself, usually by passing an index through an environment variable you set per step.
Does Bitbucket show test results?
Yes — put JUnit XML under test-results/ and the reports appear on the pipeline page automatically, with failures linked. There is no task to configure; the path convention is the whole integration.

Runnable samples for this page

last test results ↗

Working tests, not fragments — they run in CI on every push to 8exgh/endtoendtester-samples.

Was this page useful?