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
# 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.shYAML 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.
// jest.config.js
reporters: [
'default',
['jest-junit', { outputDirectory: 'test-results', outputName: 'junit.xml' }]
]// 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:
- 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 automaticallyBuild 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
definitions:
caches:
playwright: ~/.cache/ms-playwright
gradle: ~/.gradle/cachesBitbucket'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 ↗- YAML / Groovy / Kotlin
pipelines/ci-cd/bitbucket-pipelines
Working tests, not fragments — they run in CI on every push to 8exgh/endtoendtester-samples.
Was this page useful?
Related topics
- Testing in GitHub ActionsA complete pipeline — unit tests, integration with containers, sharded Playwright, coverage and artefacts — plus the caching and concurrency settings that make it fast.
- Testing in JenkinsDeclarative pipelines, parallel stages, agent control and JUnit reporting — how to run a modern test suite on the CI server you probably inherited.
- Playwright ShardingSplitting a browser suite across machines and merging the reports back — the mechanics, the prerequisites, and how to choose a shard count that is actually faster.
- Test ReportingGetting results out of the runner and in front of people — JUnit XML, pull request annotations, per-test history, and the numbers worth publishing.