Os binários de navegador do Playwright dependem de cerca de duas dezenas de bibliotecas de sistema. Incompatibilidades entre as versões dessas bibliotecas no seu laptop e no CI causam falhas que não têm nada a ver com os seus testes. A imagem oficial do Playwright em mcr.microsoft.com/playwright empacota os três navegadores e todas as dependências em uma única imagem que roda de forma idêntica em qualquer lugar. A tag da imagem deve corresponder à versão do @playwright/test no package.json.
Por que o Docker importa para QA
Suites de testes de navegador são excepcionalmente sensíveis ao ambiente. O Playwright instala binários de Chromium, Firefox e WebKit que dependem de bibliotecas de sistema específicas: libglib, libnss, libatk, e mais umas duas dezenas. No seu laptop essas bibliotecas provavelmente estão na versão certa porque você instalou o Playwright recentemente. Na máquina de um colega de 2022, pode não estar. Em um runner de CI provisionado seis meses atrás com uma imagem Ubuntu diferente, a incompatibilidade vira uma falha de build às 2 da manhã.
A correção tradicional é uma página de wiki chamada "Dev Setup" que fica desatualizada no momento em que alguém a escreve. A correção do Docker é um arquivo no repositório chamado Dockerfile que descreve o ambiente exato que os testes precisam. Qualquer pessoa que rodar docker build obtém o mesmo ambiente, independente do sistema operacional ou do que instalou na semana passada.
Para QA especificamente, o Docker traz três benefícios concretos. Primeiro, paridade de ambiente: os testes rodam dentro do mesmo container localmente e no CI, então um teste passando localmente significa alguma coisa. Segundo, sem overhead de setup: um novo membro do time roda toda a suite de testes com dois comandos sem instalar Node, Playwright ou dependências de navegador. Terceiro, isolamento: as execuções de teste não interferem entre si nem com o que está instalado na máquina host.
Conceitos básicos de Docker para testers
Se você nunca trabalhou com Docker, o modelo mental é direto. Uma imagem é um snapshot de um sistema de arquivos: um sistema operacional, pacotes instalados, seu código e variáveis de ambiente, tudo congelado em um momento no tempo. Um container é uma instância em execução de uma imagem: pense na imagem como uma classe e no container como um objeto.
Você constrói uma imagem a partir de um Dockerfile, um arquivo de texto que lista os passos para construí-la. Você roda um container a partir de uma imagem com docker run. Quando o container termina, ele para. Nada que ele fez muda a imagem.
# Baixar uma imagem existente de um registry
docker pull node:20-slim
# Rodar um container a partir dessa imagem e executar um comando
docker run node:20-slim node --version
# Construir uma imagem a partir de um Dockerfile no diretório atual
docker build -t my-playwright-tests .
# Rodar um container a partir da imagem que você acabou de construir
docker run my-playwright-testsO conceito-chave para fluxos de teste é que containers são efêmeros por padrão. Arquivos criados dentro de um container somem quando ele para, incluindo seus relatórios de teste. Isso se resolve com volume mounts, que vamos cobrir na seção de execução de testes.
Imagens são compostas de camadas. Cada instrução em um Dockerfile cria uma nova camada, e o Docker faz cache das camadas que não mudaram. É isso que torna os rebuilds rápidos. Se você muda o código de teste mas não as dependências, o Docker reutiliza a camada em cache e reconstrói apenas o que mudou.
A imagem oficial do Playwright para Docker
O time do Playwright publica e mantém uma imagem Docker oficial em mcr.microsoft.com/playwright. Essa imagem vem com tudo que os testes de navegador precisam: Node.js, o próprio pacote Playwright, e os três binários de navegador (Chromium, Firefox, WebKit) com as dependências de bibliotecas de sistema pré-instaladas.
# Baixar a imagem correspondente à sua versão do Playwright
docker pull mcr.microsoft.com/playwright:v1.52.0-jammy
# Ver o que tem dentro
docker run --rm mcr.microsoft.com/playwright:v1.52.0-jammy node --version
docker run --rm mcr.microsoft.com/playwright:v1.52.0-jammy npx playwright --versionO formato da tag é v{versão-playwright}-{codinome-ubuntu}. jammy é Ubuntu 22.04, a opção mais estável e amplamente usada. Uma variante noble para Ubuntu 24.04 também está disponível.
A imagem é grande (cerca de 1,8GB) porque contém três instalações completas de navegador. Esse tamanho é o preço de ter tudo pré-instalado sem precisar rodar playwright install --with-deps na inicialização do container. Para ambientes de CI onde a imagem é baixada repetidamente, é um bom tradeoff. O cache de camadas do Docker significa que você só baixa uma vez por runner, a menos que a tag mude.
A regra crítica: a tag da imagem deve corresponder à sua versão do @playwright/test no package.json. Se você usa v1.52.0-jammy mas o projeto tem "@playwright/test": "1.50.0", as incompatibilidades de API vão causar falhas confusas. Fixe as duas.
Escrevendo um Dockerfile para o seu projeto Playwright
Comece com a imagem oficial do Playwright como base. Aqui está um Dockerfile completo, pronto para produção:
# Use a imagem oficial do Playwright — fixe a versão para corresponder ao package.json
FROM mcr.microsoft.com/playwright:v1.52.0-jammy
# Defina o diretório de trabalho dentro do container
WORKDIR /app
# Copie os arquivos de pacotes primeiro para melhor cache de camadas
# Esta camada só é reconstruída quando as dependências mudam
COPY package.json package-lock.json ./
# Instale as dependências do projeto (os navegadores já estão na imagem base)
RUN npm ci
# Copie o restante do projeto
COPY . .
# Comando padrão: rodar todos os testes
CMD ["npx", "playwright", "test"]A ordem das instruções COPY e RUN importa para o cache. Copiar package.json e package-lock.json antes do código-fonte garante que a camada do npm ci fica em cache. Ela é reutilizada sempre que você muda arquivos de teste sem tocar nas dependências. Se você copiasse tudo primeiro e depois rodasse npm ci, qualquer mudança em qualquer arquivo invalidaria o cache de dependências.
Note que não há passo npx playwright install. A imagem base já tem os navegadores. Isso torna o build significativamente mais rápido do que um setup que começa de uma imagem Node simples.
Adicione um arquivo .dockerignore junto do Dockerfile para excluir arquivos que não pertencem à imagem:
# .dockerignore
node_modules
playwright-report
test-results
.git
.github
*.mdExcluir node_modules é especialmente importante. Sem isso, o Docker copiaria seu node_modules local para a imagem antes de rodar o npm ci. Isso desperdiça tempo e pode introduzir binários específicos de plataforma que não funcionam dentro do container Linux.
Construa a imagem:
docker build -t playwright-tests:local .A flag -t nomeia a imagem. playwright-tests:local é uma convenção para distinguir imagens construídas localmente de imagens de registry.
Rodando testes em um container
Com a imagem construída, rodar os testes é um comando:
docker run --rm playwright-tests:local--rm remove automaticamente o container quando termina. Sem ele, containers parados se acumulam e consomem espaço em disco.
O problema é que os relatórios de teste são escritos dentro do container e somem quando ele para. Resolva com um volume mount usando -v:
docker run --rm \
-v $(pwd)/playwright-report:/app/playwright-report \
-v $(pwd)/test-results:/app/test-results \
playwright-tests:localA flag -v mapeia um diretório do host para um diretório do container. Quando o Playwright escreve o relatório HTML em /app/playwright-report dentro do container, ele na verdade escreve em $(pwd)/playwright-report na sua máquina host. Após o container parar, o relatório está lá, pronto para abrir.
No Windows, substitua $(pwd) por %cd% no Command Prompt ou ${PWD} no PowerShell:
docker run --rm `
-v ${PWD}/playwright-report:/app/playwright-report `
-v ${PWD}/test-results:/app/test-results `
playwright-tests:localPara rodar um subconjunto de testes, sobrescreva o CMD padrão adicionando argumentos:
# Rodar apenas testes com uma tag
docker run --rm playwright-tests:local npx playwright test --grep @smoke
# Rodar apenas Chromium
docker run --rm playwright-tests:local npx playwright test --project=chromium
# Rodar um arquivo específico
docker run --rm playwright-tests:local npx playwright test tests/login.spec.tsPasse variáveis de ambiente com -e:
docker run --rm \
-e BASE_URL=https://staging.example.com \
-e TEST_USER=testuser \
-e TEST_PASSWORD=secret \
-v $(pwd)/playwright-report:/app/playwright-report \
playwright-tests:localpackage.json para o comando docker run completo para que o time não precise lembrar das flags de volume mount. npm run test:docker é mais fácil de documentar e mais difícil de digitar errado do que o comando completo.docker-compose para execuções de teste locais
Quando os testes rodam contra uma aplicação, você precisa da aplicação e do container de testes rodando ao mesmo tempo com acesso de rede entre eles. O docker-compose gerencia setups de múltiplos containers com um único arquivo.
Aqui está um docker-compose.yml para um setup típico onde os testes Playwright rodam contra uma aplicação web rodando localmente:
# docker-compose.yml
version: '3.8'
services:
# Sua aplicação sendo testada
app:
image: your-app:latest
ports:
- '3000:3000'
environment:
NODE_ENV: test
DATABASE_URL: postgres://user:pass@db:5432/testdb
depends_on:
db:
condition: service_healthy
healthcheck:
test: ['CMD', 'curl', '-f', 'http://localhost:3000/health']
interval: 5s
timeout: 5s
retries: 10
# Banco de dados (se a aplicação precisar)
db:
image: postgres:16
environment:
POSTGRES_USER: user
POSTGRES_PASSWORD: pass
POSTGRES_DB: testdb
healthcheck:
test: ['CMD-SHELL', 'pg_isready -U user -d testdb']
interval: 3s
timeout: 3s
retries: 15
# Container de testes Playwright
tests:
build:
context: .
dockerfile: Dockerfile
environment:
BASE_URL: http://app:3000
depends_on:
app:
condition: service_healthy
volumes:
- ./playwright-report:/app/playwright-report
- ./test-results:/app/test-results
# Não iniciar os testes automaticamente — rodar sob demanda
profiles:
- testingO profiles: [testing] no serviço de testes significa que ele não vai iniciar quando você rodar docker-compose up sozinho (que iniciaria a aplicação e o banco). Para rodar os testes:
# Iniciar a aplicação e o banco
docker-compose up -d app db
# Rodar os testes
docker-compose run --rm tests
# Ou iniciar tudo e rodar os testes em um comando
docker-compose --profile testing run --rm tests
# Encerrar tudo
docker-compose downOs serviços se comunicam usando seus nomes de serviço como hostnames. O container de testes define BASE_URL: http://app:3000, e o DNS interno do Docker resolve app para o endereço IP do container da aplicação. Seu playwright.config.ts lê process.env.BASE_URL, então localmente ele acessa localhost:3000 e no container acessa http://app:3000.
O depends_on com condition: service_healthy é importante. Sem ele, o Playwright iniciaria antes da aplicação estar pronta para aceitar conexões e todo teste falharia com um erro de conexão. O healthcheck faz polling até a aplicação responder, e só então o Docker inicia o container dependente.
Integrando Docker no GitHub Actions
O GitHub Actions pode usar um container Docker como ambiente de execução para um job inteiro, ou você pode rodar docker build e docker run como steps. Para o Playwright especificamente, usar o container diretamente como ambiente do job é a abordagem mais limpa.
# .github/workflows/playwright.yml
name: Playwright Tests
on:
push:
branches: [main, develop]
pull_request:
branches: [main, develop]
jobs:
test:
runs-on: ubuntu-latest
timeout-minutes: 60
# Usa a imagem do Playwright como container do job
container:
image: mcr.microsoft.com/playwright:v1.52.0-jammy
options: --user 1001
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Install dependencies
run: npm ci
- name: Run Playwright tests
run: npx playwright test
env:
BASE_URL: ${{ vars.BASE_URL }}
TEST_USER: ${{ secrets.TEST_USER }}
TEST_PASSWORD: ${{ secrets.TEST_PASSWORD }}
- name: Upload test report
uses: actions/upload-artifact@v4
if: always()
with:
name: playwright-report
path: playwright-report/
retention-days: 14O bloco container: no nível do job diz ao GitHub Actions para rodar cada step dentro do container Docker especificado em vez de diretamente no runner Ubuntu. Os navegadores já estão instalados na imagem, então você pula o step npx playwright install completamente.
--user 1001 define o usuário do container para corresponder ao UID do runner do GitHub Actions. Sem isso, erros de permissão de arquivo podem ocorrer quando o Actions tenta escrever artifacts.
Para times que também querem construir e fazer push da própria imagem de testes para um registry:
jobs:
build-and-test:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Build test image
run: docker build -t playwright-tests:${{ github.sha }} .
- name: Run tests
run: |
docker run --rm \
-e BASE_URL=${{ vars.BASE_URL }} \
-e TEST_USER=${{ secrets.TEST_USER }} \
-e TEST_PASSWORD=${{ secrets.TEST_PASSWORD }} \
-v ${{ github.workspace }}/playwright-report:/app/playwright-report \
playwright-tests:${{ github.sha }}
- name: Upload test report
uses: actions/upload-artifact@v4
if: always()
with:
name: playwright-report
path: playwright-report/
retention-days: 14Usar ${{ github.sha }} como tag da imagem vincula a imagem ao commit exato. Isso facilita correlacionar execuções de teste com versões de código quando fazendo push para um registry.
Problemas comuns e como resolver
Containers Docker introduzem um pequeno conjunto de problemas que aparecem regularmente quando se roda Playwright. Aqui está o que esperar e como lidar com cada um.
Erros de permissão de arquivo nos relatórios. Quando o Playwright escreve em um diretório montado por volume, os arquivos pertencem ao usuário com que o container roda (frequentementeroot). Seu usuário host não consegue modificá-los ou deletá-los sem sudo. A correção é definir o usuário do container em tempo de execução:
docker run --rm \
--user $(id -u):$(id -g) \
-v $(pwd)/playwright-report:/app/playwright-report \
playwright-tests:localIsso roda o container como seu usuário host atual, então os arquivos de relatório pertencem a você. No CI, use --user 1001 como mostrado no workflow acima.
# Lento: qualquer mudança de arquivo invalida o cache do npm ci
COPY . .
RUN npm ci
# Rápido: só mudanças nos arquivos de pacotes invalidam o cache do npm ci
COPY package.json package-lock.json ./
RUN npm ci
COPY . .No CI, o cache de camadas do Docker exige configuração explícita. Adicione isso ao workflow:
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build test image
uses: docker/build-push-action@v6
with:
context: .
load: true
tags: playwright-tests:${{ github.sha }}
cache-from: type=gha
cache-to: type=gha,mode=maxAs linhas cache-from: type=gha e cache-to: type=gha,mode=max usam o armazenamento de cache do GitHub Actions para cache de camadas do Docker. Sem isso, cada execução de CI constrói a imagem do zero.
localhost se refere ao próprio container, não à máquina host. Se a aplicação está rodando no host em localhost:3000, o container não consegue alcançá-la nesse endereço. Use o IP do bridge Docker do host (172.17.0.1 no Linux, host.docker.internal no Mac e Windows):
docker run --rm \
-e BASE_URL=http://host.docker.internal:3000 \
-v $(pwd)/playwright-report:/app/playwright-report \
playwright-tests:localUsando docker-compose, esse problema desaparece porque todos os serviços compartilham uma rede e se comunicam pelo nome do serviço.
FAQ
Preciso do Docker se já estou usando GitHub Actions?Não necessariamente. Os runners do GitHub Actions funcionam bem para Playwright sem Docker: instale Node, rode playwright install --with-deps e pronto. O Docker se torna valioso quando você quer o mesmo ambiente localmente e no CI, ou quando os testes rodam contra uma stack de múltiplos serviços. Também é útil ao integrar novos membros que precisam rodar os testes sem setup manual.
Comece com a imagem oficial como base do FROM. Você recebe atualizações de versões de navegador gratuitas quando sobe a tag, e a imagem é mantida por pessoas que conhecem as dependências do Playwright. Construa em cima dela apenas quando precisar de adições específicas do projeto, como fontes customizadas, uma versão específica do Node ou dados de teste pré-carregados.
Monte o diretório test-results e habilite gravação de vídeo no config do Playwright para testes que falham. Após a execução, a pasta test-results/ na sua máquina host vai conter vídeos e traces. Abra o trace com npx playwright show-trace test-results/.../trace.zip para ver exatamente o que aconteceu.
Sim. A configuração workers do Playwright controla o paralelismo e funciona igual dentro de um container. O limite prático é a contagem de CPUs do container. Para suites grandes, rode múltiplos containers em paralelo (via matrix sharding do GitHub Actions) em vez de maximizar workers dentro de um único container. É mais fácil de raciocinar e mais fácil de escalar.
Isso geralmente significa incompatibilidade de versão entre a tag da imagem e a versão do @playwright/test. Verifique se mcr.microsoft.com/playwright:v1.52.0-jammy corresponde a "@playwright/test": "1.52.0" no package.json. Se as versões não se alinham, o Playwright procura os navegadores em um caminho que não existe na imagem.
.env para o container?
Use --env-file com o docker run:
docker run --rm \
--env-file .env.test \
-v $(pwd)/playwright-report:/app/playwright-report \
playwright-tests:localNunca faça commit de arquivos .env no repositório. Adicione .env* ao .gitignore e use secrets do CI para valores sensíveis.