- Motivação
- Cypress?
- Trade-offs
- Pré-Requisitos
- O que vamos testar
- Instalação
- Removendo o Protactor
- Cypress Test Runner
- Primeiro Teste
- Como Rodar
- Configuração
- Roteiro de Testes
- Support Commands
- Fixture
- Relatórios e Integrações
- Plugins
- Incluir Outra Spec
- Falso Negativo
- DevOps
- Paralelismo
- Analytics Dashboard
Agilidade (- tempo)
Qualidade (- bugs)
Economia de horas (- custo)
O teste E2E simula a navegação pelo usuário, validando não só a interface frontend como integração com o backend.
Teste unitário valida a qualidade do Código. E2E valida a aplicação.
JavaScritpt: Baixa curva de aprendizado, custo com treinamento reduzido.
Performance: Paralelismo, Stress Test, Load Test.
Recorder: Cypress Recorder (Chrome), Katalon Recorder.
Produtividade: Auto-reload, Spies, Stubs e Mocks.
Licença: OpenSource (Mit).
End-to-end tests, Integration tests, Unit tests.
Diferente do Selenium ou Appium, que injetam comandos exernos, o Cypress roda no mesmo contexto JS do App, com acesso instantâneo a todas as interações e eventos.
Cypress não é uma ferramenta de automação geral.
Os comandos do Cypress sempre são executados dentro de um navegador.
Nunca haverá suporte para várias guias do navegador.
Você não pode usar o Cypress para controlar dois navegadores no mesmo teste.
Git
Node
Projeto para teste: Angular Real World Example App
git clone https://github.com/gothinkster/angular-realworld-example-app
cd angular-realworld-example-app
npm install
npm run start http://localhost:4200/
Aqui temos um aplicativo Angular contendo exemplos "reais" (CRUD, autenticação, etc) de acordo com a especificação para exemplos RealWord.
Vamos adicionar o Cypress!
Abra outro terminal. Na pasta/angular-realworld-example-app/ execute:
npm install cypress --save-dev
npx cypress -vCaso tenha problemas com Proxy ou Firewall, baixe o binário e configure a variável de ambiente antes de instalar:
set CYPRESS_INSTALL_BINARY=C:\cypress.zip
npm install cypress --save-dev --verbose
Remova o pacote:
npm uninstall protactor --save-devExclua a pasta /e2e/
Do package.json, remova a linha: "e2e": "ng e2e"
npx cypress openO comando "cypress open", além de abrir o Cypress Test Runner, cria a pasta inicial /cypress/ e o arquivo de configuração /cypress.json
E já vem com /examples/ dos principais comandos Cypress.
cypress/integrations/exemplo.spec.js
describe('Primeiro Teste', () => {
it('Exemplos Cypress', () => {
cy.visit('https://example.cypress.io')
expect(true).to.equal(true)
})
})
describe and it come from Mocha
expect comes from Chai
Para executar todos os testes da pasta /cypress/integration/:
npx cypress runPara executar somente um roteiro:
npx cypress run --spec "cypress/integration/examples/example.spec.js"Para abrir a interface do Cypress Test Runner:
npx cypress open
cypress.json
{ "baseUrl": "http://localhost:4200", "pageLoadTimeout": 30000, "defaultCommandTimeOut": 30000, "viewportHeight": 800, "viewportWidth": 500, "retries": 3 }
- Cadastro
- Login
- Perfil
- Feeds
- Paginação
- Post
- Tags
- Comentários
- Seguir
- Logout
Extensão para o Chrome capaz de gravar um roteiro base.
Recomendado para capturar os seletores no DOM.
describe('Conduit Cadastro', () => {
const usuario = 'usuario' + (new Date()).getTime()
const senha = 'senha' + (new Date()).getTime()
it('Novo Usuário', () => {
cy.visit('/register')
cy.get('[formcontrolname=username]').type(usuario)
cy.get('[formcontrolname=email]').type(usuario+'@email.com')
cy.get('[formcontrolname=password]').type(senha)
cy.get('.btn').click()
cy.contains('.nav-item:nth-child(4) > .nav-link', usuario)
.should('be.visible')
})
})
npx cypress run --spec "cypress/integration/register.spec.js"
cypress/support/index.js
Cypress.Commands.add('login', (username, password) => {
cy.visit('/login')
cy.url().should('include', '/login')
cy.get('[formcontrolname=email]').type(username)
cy.get('[formcontrolname=password]').type(password)
cy.get('.btn').click()
})
cypress/integration/login.spec.js
describe('Conduit Login', () => {
it('Login sucesso', () => {
cy.login('testecypress@testecypress.com', 'testecypress')
cy.get('.nav-item:nth-child(4) > nav-link').click()
cy.get('.btn:nth-child(5)').click()
cy.url().should('contain', '/settings')
})
it('Dados Inválidos', () => {
cy.login('usuario@inexistente.com', 'senhaerrada')
cy.get('.error-messages > li')
.should('contain', 'email or password is invalid')
})
})
cypress/integration/perfil.spec.js
describe('Profile', () => {
it('Editar Perfil', () => {
cy.login('testecypress@testecypress.com', 'testecypress')
cy.contains('testecypress').click()
cy.contains('Edit Profile Settings').click()
cy.get('[formcontrolname="image"]').clear()
cy.get('[formcontrolname="image"]')
.type('https://thispersondoesnotexist.com/image')
cy.get('[formcontrolname="password"]').type('testecypress')
cy.contains('Update Settings').click()
})
})
cypress/integration/feed.spec.js
describe('Conduit Feed', () => {
it('Ver Feeds', () => {
cy.login('testecypress@testecypress.com', 'testecypress')
cy.get('.nav-pills > .nav-item:nth-child(1) > .nav-link').click();
cy.get('.nav-pills > .nav-item:nth-child(2) > .nav-link').click()
cy.get('app-article-preview:nth-child(1) .btn').click()
})
})
cypress/integration/pagination.spec.js
describe('Paginação', () => {
it('Paginar', () => {
cy.visit('/')
cy.get('.page-item.active > a').contains('1')
cy.get('.page-item:nth-child(2) > .page-link').click()
cy.get('.page-item.active > a').contains('2')
cy.get('.page-item:nth-child(3) > .page-link').click()
cy.get('.page-item.active > a').contains('3')
})
})
cypress/integration/post.spec.js
describe('Post', () => {
beforeEach(() => {
cy.login('testecypress@testecypress.com', 'testecypress')
})
it('Novo', () => {
const tit = 'Cypress E2E'
cy.contains('New Article').click()
cy.location('pathname').should('equal', '/editor')
cy.get('[formcontrolname=title]').type(tit)
cy.get('[formcontrolname=description]').type('Ponta a Ponta')
cy.get('[formcontrolname=body]').type('Agilidade, Qualidade')
cy.contains('Publish Article').click()
cy.get('h1').contains(tit)
})
it('Editar', () => {
cy.contains('testecypress').click()
cy.location('pathname').should('contains', '/profile')
cy.get('.article-preview').get('h1').first().click()
cy.contains('Edit Article').click()
cy.get('[formcontrolname=body]').clear()
cy.get('[formcontrolname=body]').type('Economia')
cy.contains('Publish Article').click()
cy.contains('Economia')
})
})
cypress/integration/tags.spec.js
describe('Tags', () => {
it('Adicionar', () => {
cy.login('testecypress@testecypress.com', 'testecypress')
cy.contains('testecypress').click()
cy.location('pathname').should('contains', '/profile')
cy.get('.article-preview').get('h1').first().click()
cy.contains('Edit Article').click()
cy.get('[placeholder="Enter tags"]').type('dungeons{enter}');
cy.get('[placeholder="Enter tags"]').type('dragons{enter}');
cy.contains('Publish Article').click();
cy.get('.tag-list').contains('dragons');
})
})
cypress/integration/comentarios.spec.js
describe('Comentarios', () => {
it('Escrever', () => {
cy.login('testecypress@testecypress.com', 'testecypress')
cy.contains('Global Feed').click()
cy.get('.preview-link').first().click()
cy.get('.form-control').type('Sensacional!')
cy.get('.btn-primary').click()
cy.contains('Sensacional!')
})
})
cypress/integration/seguir.spec.js
describe('Seguir', () => {
it('Seguir Usuário', () => {
const usuario = 'usuario'+(new Date()).getTime();
const senha = 'senha'+(new Date()).getTime();
cy.visit('/register', { timeout: 10000 })
cy.get('[formcontrolname=username]').type(usuario)
cy.get('[formcontrolname=email]').type(usuario+'@email.com')
cy.get('[formcontrolname=password]').type(senha)
cy.get('.btn').click()
cy.wait(10000)
cy.visit('/profile/testecypress')
cy.contains('Folow').click()
})
})
cypress/integration/logout.spec.js
describe('Logout', () => {
it('Logout via Perfil', () => {
cy.login('testecypress@testecypress.com', 'testecypress')
cy.contains('Settings').click()
cy.url().should('include', '/settings')
cy.get('.btn-outline-danger').click()
})
})
cypress/support/index.js
Cypress.Commands.add('loadUsers', () => {
cy.fixture('users')
.as('users')
})
cypress/integration/test.spec.js
// this.users.default.username
// this.users.default.pass
// this.users.client.username
// this.users.client.pass
cypress/fixtures/users.json
{
"default":{
"user": "basic-user",
"pass": "testpass"
},
"client":{
"user": "premium-user",
"pass": "testpass"
}
}
Funcionalidades extendidas
Reports json, html e xml
- mocha
- mochawesome
- mochawesome-merge
- mochawesome-report-generator
- cypress-multi-reporters
- cypress-slack-reporter
- cypress-sonarqube-reporter
Rede, microserviços e dependências podem falhar
Um teste E2E pode falhar por conta de outros recursos além do controle da sua aplicação:
- Uma API pode falhar
- Insdisponibilidade de Rede ou Firewall
- Indisponibilidade de recursos como CPU e memória
- Deploys paralelos
Verifique se os as apis e serviços estão rodando:
it('Backend Health Checks', () => {
cy.request('https://login/healthcheck').then((response) => {
expect(response.status).to.eq(200)
})
cy.request('https://database/healtcheck')
.then((response) => {
expect(response.status).to.eq(200)
})
})
Environments:
- CYPRESS_BASE_URL
- CYPRESS_VIDEO_COMPRESSION
- CYPRSS_REPORTER
- CYPRESS_INSTALL_BINARY
- CYPRESS_RECORD_KEY
# cypress/Dockerfile # Imagem base FROM cypress/base:14.0.0 # Copia os arquivos para a imagem COPY ./ # Opcional - Pega o binário local #ENV CYPRESS_INSTALL_BINARY=/cypress.zip RUN npm install --verbose RUN $(npm bin)/cypress verify # Inicialização do container CMD $(npm bin)/cypress run
# prompt / terminal docker build -t cypress:0.0.1 docker run cypress:0.0.1
cypress run --parallel --record
cypress run --record --key=abc123
1 - Cypress Kitchen Sink
git clone https://github.com/cypress-io/cypress-example-kitchensink.git cd cypress-example-kitchensink npm install npm start npm run cy:open
2 - Cypress Real Word App
git clone https://github.com/cypress-io/cypress-realworld-app.git cd cypress-realworld-app npm install npm dev npm run cypress:open