Curso intermediário de automação de testes com Cypress da Escola Talking About Testing.
- Aula 1 - Setup do ambiente local com Docker
- Aula 2 - Setup do projeto de testes com Cypress
- Aula 3 - Testes simples de GUI
- Aula 4 - Testes intermediários de GUI
- Aula 5 - Testes de API
- Aula 6 - Otimizando os testes de GUI
- Aula 7 - Testes com muitas pré-condições
- Aula 8 - Executando comandos a nível de sistema
- Aula 9 - Executando todos os testes
- Desligando o container
Execute o comando docker run --publish 80:80 --publish 22:22 --hostname localhost wlsf82/gitlab-ce
e aguarde até o ambiente inicializar (isso pode levar alguns minutos), e então acesse a URL http://localhost/ para definir a senha o usuário root
.
- Faça login com o usuário
root
com a senha definida na seção anterior - Clique no avatar do usuário no canto superior direito da tela, clique no link Settings, e então clique o menu lateral esquerdo na opção Access Tokens
- No campo nome, digite o valor
curso-cypress-intermediario
, na seção Scopes marque a opção 'api', e então clique no botão 'Create personal access token'
Uma mensagem de que o token foi criado com sucesso deve ser exibida, além do token propriamente dito. Copie o token clicando no botão à direita do campo e guarde-o para utilizar na aula 2.
- No terminal, digite o seguinte comando e pressione ENTER
ssh-keygen -t ed25519 -C "root@example.com"
- Será solicitado um caminho para salvar a chave. Pressione ENTER para aceitar o caminho padrão
- Será solicitada uma senha. Pressione ENTER para que a senha não seja necessária
- Será solicitado que repita a senha. Pressione ENTER novamente para que a senha não seja necessária
- No terminal, digite o seguinte comando e pressione ENTER para copiar a chave pública recém criada para a área de transferência
pbcopy < ~/.ssh/id_ed25519.pub
- Logado na aplicação com o usuário
root
, clique no avatar do usuário no canto superior direito da tela, clique no link Settings, e então clique no menu lateral esquerdo na opção SSH Keys - Cole sua chave SSH pública no campo key. O campo Title deve ser automaticamente preenchido
- Por fim, clique no botão Add key
Você também encontrará instruções sobre como gerar a chave SSH em sistema operacional Windows na própria aplicação em teste a partir da seguinte URL http://localhost/help/ssh/README#generating-a-new-ssh-key-pair (instruções em Inglês).
- Acesse a URL https://gitlab.com/wlsf82/curso-cypress-intermediario
- Clique no botão 'Clone'
- Escolha uma das opções (Clone with SSH ou Clone with HTTPS) e então clique no botão Copy URL ao lado do campo da opção escolhida
- No terminal, no diretório onde você armazena seus projetos de software, digite `git clone [URL copiada no passo anteior] e pressione ENTER
- Por fim, acesso o diretório do projeto recém clonado (
cd curso-cypress-intermediario/
)
No terminal, dentro do diretório curso-cypress-intermediario/
, execute o comando npm init -y
(este comando irá criar o arquivo package.json
na raiz do projeto)
No mesmo diretório, crie um arquivo chamado .gitignore
, com o seguinte conteúdo:
.DS_Store
cypress.env.json
cypress/screenshots/
cypress/videos/
node_modules/
temp/
Na raiz do projeto, crie um diretório chamado temp/
. Este diretório será utilizado posteriormente para o teste de clone de projeto.
No terminal, na raiz do projeto, execute o comando npm i cypress@9.7.0 -D
(este comando irá instalar o Cypress como dependência de desenvolvimento, além de criar o arquivo package-lock.json
e o diretório node_modules/
)
No terminal, na raiz do projeto, execute o comando npx cypress open
(este comando irá abrir o Cypress em modo interativo e irá criar a estrutura inicial para os testes automatizados)
- Feche a aplicação Electron do Cypress
- Abra o arquivo
cypress.json
criado na raiz do projeto e altere seus conteúdo pelo seguinte:
{
"baseUrl": "http://localhost/"
}
- Ainda na raiz do projeto, crie um arquivo chamado
cypress.env.json
com os seguintes dados:
{
"user_name": "root",
"user_password": "password-do-usuario-root-definido-anteriormente",
"gitlab_access_token": "access-token-criado-anteriormente"
}
- Por fim, dentro do diretório
cypress/integration/
, delete o diretórioexamples/
- Dentro do diretório
cypress/integration/
, crie um novo diretório chamadogui
(graphical user interface) - Dentro do diretrório
cypress/integration/gui/
, crie um arquivo chamadologin.spec.js
com os seguintes dados:
/// <reference types="Cypress" />
describe('Login', () => {
it('successfully', () => {
cy.login()
cy.get('.qa-user-avatar').should('exist')
})
})
- Dentro do diretório
cypress/support/
, renomeie o arquivocommands.js
porgui_commands.js
e altere seu conteúdo pelo o seguinte:
/// <reference types="Cypress" />
Cypress.Commands.add('login', () => {
cy.visit('users/sign_in')
cy.get("[data-qa-selector='login_field']").type(Cypress.env('user_name'))
cy.get("[data-qa-selector='password_field']").type(Cypress.env('user_password'))
cy.get("[data-qa-selector='sign_in_button']").click()
})
- Dentro do diretório
cypress/support/
, altere os dados do arquivoindex.js
pelo seguinte:
import './gui_commands'
- Por fim, no terminal, na raiz do projeto, execute o comando
npx cypress run
para executar o novo teste em modo headless
- Dentro do diretrório
cypress/integration/gui/
, crie um arquivo chamadologout.spec.js
com os seguintes dados:
/// <reference types="Cypress" />
describe('Logout', () => {
beforeEach(() => cy.login())
it('successfully', () => {
cy.logout()
cy.url().should('be.equal', `${Cypress.config('baseUrl')}users/sign_in`)
})
})
- Dentro do diretório
cypress/support/
, atualize o arquivogui_commands.js
com o commandologout
, conforme abaixo:
/// <reference types="Cypress" />
Cypress.Commands.add('login', () => {
...
})
Cypress.Commands.add('logout', () => {
cy.get('.qa-user-avatar').click()
cy.contains('Sign out').click()
})
- Por fim, no terminal, na raiz do projeto, execute o comando
npx cypress run --spec cypress/integration/gui/logout.spec.js
para executar o novo teste em modo headless
-
Para o teste de criação de projeto iremos utilizar a biblioteca
faker
para a criação de dados randômicos. No terminal, na raiz do projeto, execute o comandonpm i faker@5.5.3 -D
(este comando irá instalar a bibliotecafaker
como dependência de desenvolvimento) -
Dentro do diretrório
cypress/integration/gui/
, crie um arquivo chamadocreateProject.spec.js
com os seguintes dados:
/// <reference types="Cypress" />
const faker = require('faker')
describe('Create Project', () => {
beforeEach(() => cy.login())
it('successfully', () => {
const project = {
name: `project-${faker.random.uuid()}`,
description: faker.random.words(5)
}
cy.gui_createProject(project)
cy.url().should('be.equal', `${Cypress.config('baseUrl')}${Cypress.env('user_name')}/${project.name}`)
cy.contains(project.name).should('be.visible')
cy.contains(project.description).should('be.visible')
})
})
- Dentro do diretório
cypress/support/
, atualize o arquivogui_commands.js
com o commandogui_createProject
, conforme abaixo:
/// <reference types="Cypress" />
Cypress.Commands.add('login', () => {
...
})
Cypress.Commands.add('logout', () => {
...
})
Cypress.Commands.add('gui_createProject', project => {
cy.visit('projects/new')
cy.get('#project_name').type(project.name)
cy.get('#project_description').type(project.description)
cy.get('.qa-initialize-with-readme-checkbox').check()
cy.contains('Create project').click()
})
- Por fim, no terminal, na raiz do projeto, execute o comando
npx cypress run --spec cypress/integration/gui/createProject.spec.js
para executar o novo teste em modo headless
- Dentro do diretrório
cypress/integration/gui/
, crie um arquivo chamadocreateIssue.spec.js
com os seguintes dados:
/// <reference types="Cypress" />
const faker = require('faker')
describe('Create Issue', () => {
const issue = {
title: `issue-${faker.random.uuid()}`,
description: faker.random.words(3),
project: {
name: `project-${faker.random.uuid()}`,
description: faker.random.words(5)
}
}
beforeEach(() => {
cy.login()
cy.gui_createProject(issue.project)
})
it('successfully', () => {
cy.gui_createIssue(issue)
cy.get('.issue-details')
.should('contain', issue.title)
.and('contain', issue.description)
})
})
- Dentro do diretório
cypress/support/
, atualize o arquivogui_commands.js
com o commandogui_createIssue
, conforme abaixo:
/// <reference types="Cypress" />
Cypress.Commands.add('login', () => {
...
})
Cypress.Commands.add('logout', () => {
...
})
Cypress.Commands.add('gui_createProject', project => {
...
})
Cypress.Commands.add('gui_createIssue', issue => {
cy.visit(`${Cypress.env('user_name')}/${issue.project.name}/issues/new`)
cy.get('.qa-issuable-form-title').type(issue.title)
cy.get('.qa-issuable-form-description').type(issue.description)
cy.contains('Submit issue').click()
})
- Por fim, no terminal, na raiz do projeto, execute o comando
npx cypress run --spec cypress/integration/gui/createIssue.spec.js
para executar o novo teste em modo headless
- Dentro do diretório
cypress/integration/
, crie um novo diretório chamadoapi
(application programming interface) - Dentro do diretrório
cypress/integration/api/
, crie um arquivo chamadocreateProject.spec.js
com os seguintes dados:
/// <reference types="Cypress" />
const faker = require('faker')
describe('Create Project', () => {
it('successfully', () => {
const project = {
name: `project-${faker.random.uuid()}`,
description: faker.random.words(5)
}
cy.api_createProject(project)
.then(response => {
expect(response.status).to.equal(201)
expect(response.body.name).to.equal(project.name)
expect(response.body.description).to.equal(project.description)
})
})
})
- Dentro do diretório
cypress/support/
, crie um arquivo chamadoapi_commands.js
, com os seguintes dados:
/// <reference types="Cypress" />
const accessToken = Cypress.env('gitlab_access_token')
Cypress.Commands.add('api_createProject', project => {
cy.request({
method: 'POST',
url: `/api/v4/projects/?private_token=${accessToken}`,
body: {
name: project.name,
description: project.description,
initialize_with_readme: true
}
})
})
- Dentro do diretório
cypress/support/
, adicione ao arquivoindex.js
o import do arquivoapi_commands.js
, conforme abaixo:
import './api_commands'
import './gui_commands'
- Por fim, no terminal, na raiz do projeto, execute o comando
npx cypress run --spec cypress/integration/api/createProject.spec.js
para executar o novo teste em modo headless.
- Dentro do diretrório
cypress/integration/api/
, crie um arquivo chamadocreateIssue.spec.js
com os seguintes dados:
/// <reference types="Cypress" />
const faker = require('faker')
describe('Create issue', () => {
it('successfully', () => {
const issue = {
title: `issue-${faker.random.uuid()}`,
description: faker.random.words(3),
project: {
name: `project-${faker.random.uuid()}`,
description: faker.random.words(5)
}
}
cy.api_createIssue(issue)
.then(response => {
expect(response.status).to.equal(201)
expect(response.body.title).to.equal(issue.title)
expect(response.body.description).to.equal(issue.description)
})
})
})
- Dentro do diretório
cypress/support/
, atualize o arquivoapi_commands.js
com o commandoapi_createIssue
, conforme abaixo:
/// <reference types="Cypress" />
const accessToken = Cypress.env('gitlab_access_token')
Cypress.Commands.add('api_createProject', project => {
...
})
Cypress.Commands.add('api_createIssue', issue => {
cy.api_createProject(issue.project)
.then(response => {
cy.request({
method: 'POST',
url: `/api/v4/projects/${response.body.id}/issues?private_token=${accessToken}`,
body: {
title: issue.title,
description: issue.description
}
})
})
})
- Por fim, no terminal, na raiz do projeto, execute o comando
npx cypress run --spec cypress/integration/api/createIssue.spec.js
para executar o novo teste em modo headless.
- No arquivo
cypress/integration/gui/createIssue.spec.js
, substitua o comandocy.gui_createProject(issue.project)
pelo seguintecy.api_createProject(issue.project)
. Desta forma, em vez de criarmos o projeto via GUI, o criamos via API, visto que tal opção é mais rápida, além de tornar o teste mais independente. - Por fim, no terminal, na raiz do projeto, execute o comando
npx cypress run --spec cypress/integration/gui/createIssue.spec.js
para executar o teste refatorado em modo headless
- No diretório
cypress/integration/gui/
, crie um arquivo chamadosetLabelOnIssue.spec.js
com o seguinte conteúdo:
/// <reference types="Cypress" />
const faker = require('faker')
describe('Set label on issue', () => {
const issue = {
title: `issue-${faker.random.uuid()}`,
description: faker.random.words(3),
project: {
name: `project-${faker.random.uuid()}`,
description: faker.random.words(5)
}
}
const label = {
name: `label-${faker.random.word()}`,
color: '#ffaabb'
}
beforeEach(() => {
cy.login()
cy.api_createIssue(issue)
.then(response => {
cy.api_createLabel(response.body.project_id, label)
cy.visit(`${Cypress.env('user_name')}/${issue.project.name}/issues/${response.body.iid}`)
})
})
it('successfully', () => {
cy.gui_setLabelOnIssue(label)
cy.get('.qa-labels-block').should('contain', label.name)
cy.get('.qa-labels-block span')
.should('have.attr', 'style', `background-color: ${label.color}; color: #333333;`)
})
})
- No diretório
cypress/support/
, atualize o arquivoapi_commands.js
conforme abaixo:
/// <reference types="Cypress" />
const accessToken = Cypress.env('gitlab_access_token')
Cypress.Commands.add('api_createProject', project => {
...
})
Cypress.Commands.add('api_createIssue', issue => {
...
})
Cypress.Commands.add('api_createLabel', (projectId, label) => {
cy.request({
method: 'POST',
url: `/api/v4/projects/${projectId}/labels?private_token=${accessToken}`,
body: {
name: label.name,
color: label.color
}
})
})
- No diretório
cypress/support/
, atualize o arquivogui_commands.js
conforme abaixo:
/// <reference types="Cypress" />
Cypress.Commands.add('login', () => {
...
})
Cypress.Commands.add('logout', () => {
...
})
Cypress.Commands.add('gui_createProject', project => {
...
})
Cypress.Commands.add('gui_createIssue', issue => {
...
})
Cypress.Commands.add('gui_setLabelOnIssue', label => {
cy.get('.qa-edit-link-labels').click()
cy.contains(label.name).click()
cy.get('body').click()
})
- Por fim, no terminal, na raiz do projeto, execute o comando
npx cypress run --spec cypress/integration/gui/setLabelOnIssue.spec.js
para executar o novo teste em modo headless
- No diretório
cypress/integration/gui/
, crie um arquivo chamadosetMilestoneOnIssue.spec.js
com o seguinte conteúdo:
/// <reference types="Cypress" />
const faker = require('faker')
describe('Set milestone on issue', () => {
const issue = {
title: `issue-${faker.random.uuid()}`,
description: faker.random.words(3),
project: {
name: `project-${faker.random.uuid()}`,
description: faker.random.words(5)
}
}
const milestone = {
title: `milestone-${faker.random.word()}`
}
beforeEach(() => {
cy.login()
cy.api_createIssue(issue)
.then(response => {
cy.api_createMilestone(response.body.project_id, milestone)
cy.visit(`${Cypress.env('user_name')}/${issue.project.name}/issues/${response.body.iid}`)
})
})
it('successfully', () => {
cy.gui_setMilestoneOnIssue(milestone)
cy.get('.block.milestone').should('contain', milestone.title)
})
})
- No diretório
cypress/support/
, atualize o arquivoapi_commands.js
conforme abaixo:
/// <reference types="Cypress" />
const accessToken = Cypress.env('gitlab_access_token')
Cypress.Commands.add('api_createProject', project => {
...
})
Cypress.Commands.add('api_createIssue', issue => {
...
})
Cypress.Commands.add('api_createLabel', (projectId, label) => {
...
})
Cypress.Commands.add('api_createMilestone', (projectId, milestone) => {
cy.request({
method: 'POST',
url: `/api/v4/projects/${projectId}/milestones?private_token=${accessToken}`,
body: { title: milestone.title }
})
})
- No diretório
cypress/support/
, atualize o arquivogui_commands.js
conforme abaixo:
/// <reference types="Cypress" />
Cypress.Commands.add('login', () => {
...
})
Cypress.Commands.add('logout', () => {
...
})
Cypress.Commands.add('gui_createProject', project => {
...
})
Cypress.Commands.add('gui_createIssue', issue => {
...
})
Cypress.Commands.add('gui_setLabelOnIssue', label => {
...
})
Cypress.Commands.add('gui_setMilestoneOnIssue', milestone => {
cy.get('.block.milestone .edit-link').click()
cy.contains(milestone.title).click()
})
- Por fim, no terminal, na raiz do projeto, execute o comando
npx cypress run --spec cypress/integration/gui/setMilestoneOnIssue.spec.js
para executar o novo teste em modo headless
- No diretório
cypress/integration/
, crie um novo diretório chamadocli/
(command line interface) - No diretório
cypress/integration/cli/
, crie um arquivo chamadogitClone.spec.js
com o seguinte conteúdo:
/// <reference types="Cypress" />
const faker = require('faker')
describe('git clone', () => {
const project = {
name: `project-${faker.random.uuid()}`,
description: faker.random.words(5)
}
beforeEach(() => cy.api_createProject(project))
it('successfully', () => {
cy.cloneViaSSH(project)
cy.readFile(`temp/${project.name}/README.md`)
.should('contain', `# ${project.name}`)
.and('contain', project.description)
})
})
- No diretório
cypress/support/
, crie um arquivo chamadocli_commands.js
com o seguinte conteúdo:
/// <reference types="Cypress" />
Cypress.Commands.add('cloneViaSSH', project => {
const domain = Cypress.config('baseUrl').replace('http://', '').replace('/', '')
cy.exec(`cd temp/ && git clone git@${domain}:${Cypress.env('user_name')}/${project.name}.git`)
})
- Dentro do diretório
cypress/support/
, adicione ao arquivoindex.js
o import do arquivocli_commands.js
, conforme abaixo:
import './api_commands'
import './cli_commands'
import './gui_commands'
- Por fim, no terminal, na raiz do projeto, execute o comando
npx cypress run --spec cypress/integration/cli/gitClone.spec.js
para executar o novo teste em modo headless
Obs.: Na primeira vez que você executar o teste o seguinte será solicitado:
Are you sure you want to continue connecting (yes/no)?
Respondayes
e pressione ENTER.
Obs.2: Caso o teste falhe com o erro abaixo, execute o seguinte comando
ssh-keygen -R localhost
, pressione ENTER, e então execute o teste novamente (npx cypress run --spec cypress/integration/cli/gitClone.spec.js
):
CypressError: cy.exec('cd temp/ && git clone git@localhost:root/project-8074da23-f979-4555-84e8-7a63fb69a326.git') failed because the command exited with a non-zero code.
Pass {failOnNonZeroExit: false} to ignore exit code failures.
Information about the failure:
Code: 128
Stderr:
Cloning into 'project-8074da23-f979-4555-84e8-7a63fb69a326'...
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @
@@@@@@@@@@@@...
at Object.cypressErr (http://localhost/__cypress/runner/cypress_runner.js:106136:11)
at Object.throwErr (http://localhost/__cypress/runner/cypress_runner.js:106091:18)
at Object.throwErrByPath (http://localhost/__cypress/runner/cypress_runner.js:106123:17)
at http://localhost/__cypress/runner/cypress_runner.js:90175:23
at tryCatcher (http://localhost/__cypress/runner/cypress_runner.js:140400:23)
at Promise._settlePromiseFromHandler (http://localhost/__cypress/runner/cypress_runner.js:138336:31)
at Promise._settlePromise (http://localhost/__cypress/runner/cypress_runner.js:138393:18)
at Promise._settlePromise0 (http://localhost/__cypress/runner/cypress_runner.js:138438:10)
at Promise._settlePromises (http://localhost/__cypress/runner/cypress_runner.js:138517:18)
at Async../node_modules/bluebird/js/release/async.js.Async._drainQueue (http://localhost/__cypress/runner/cypress_runner.js:135125:16)
at Async../node_modules/bluebird/js/release/async.js.Async._drainQueues (http://localhost/__cypress/runner/cypress_runner.js:135135:10)
at Async.drainQueues (http://localhost/__cypress/runner/cypress_runner.js:135009:14)
- Abra o arquivo
package.json
localizado na raiz do projeto - Na seção
scripts
, altere o valor do scripttest
paracypress run
A seção scripts
do arquivo package.json
deve estar conforme abaixo:
"scripts": {
"test": "cypress run"
},
- Por fim, no terminal, na raiz do projeto, execute o comando
npm test
para executar todos os testes em modo headless. Você deve obter um resultado conforme demonstrado na imagem abaixo.
- No terminal, na raiz do projeto, execute o comando
npx cypress open
(este comando irá abrir a aplicação Electron do Cypress) - Para executar todos os testes em modo interativo, clique no botão 'Run all specs'. Ou, para executar um arquivo de testes em específico, clique no mesmo na listagem de testes.
- No terminal, execute o comando
docker container ls
, pressione ENTER e copie oCONTAINER ID
referente à imagenwlsf82/gitlab-ce
- Por fim, execute o comando
docker container stop [CONTAINER ID copiado no passo anterior]
e pressione ENTER
Observação: Após desligar o container, caso você queira inicilizar a aplicação novamente, siga os passo descritos na aula 1 e lembre-se de atualizar os valores no arquivo
cypress.env.json
, conforme descrito na aula 2, passo 3
Made with 💚 by Talking About Testing