Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/lucasrmagalhaes/cypresstest-angular

Utilizando o Cypress E2E para testar um blog em Angular.
https://github.com/lucasrmagalhaes/cypresstest-angular

angular cypress dio

Last synced: 2 days ago
JSON representation

Utilizando o Cypress E2E para testar um blog em Angular.

Awesome Lists containing this project

README

        


Testes E2E com Cypress

Programação


  • 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


Motivação


     Agilidade (- tempo)

     Qualidade (- bugs)

     Economia de horas (- custo)


Por que não somente Testes Unitários?


     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.



Cypress?


     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.


Trade-offs


     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.



Pré-Requisitos


     Git

     Node



O que vamos testar


     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!



Instalação


     Abra outro terminal. Na pasta/angular-realworld-example-app/ execute:

npm install cypress --save-dev

npx cypress -v

     Caso 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



Removendo o Protactor


     Remova o pacote:

npm uninstall protactor --save-dev

     Exclua a pasta /e2e/

     Do package.json, remova a linha: "e2e": "ng e2e"



Cypress Test Runner


npx cypress open

     O 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.



Primeiro Teste


     cypress/integrations/exemplo.spec.js

```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



Como Rodar


     Para executar todos os testes da pasta /cypress/integration/:

npx cypress run

     Para 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


Configuração


     cypress.json


{
"baseUrl": "http://localhost:4200",
"pageLoadTimeout": 30000,
"defaultCommandTimeOut": 30000,
"viewportHeight": 800,
"viewportWidth": 500,
"retries": 3
}


Roteiro de Testes


  1. Cadastro

  2. Login

  3. Perfil

  4. Feeds

  5. Paginação

  6. Post

  7. Tags

  8. Comentários

  9. Seguir

  10. Logout


Cypress Recorder


     Extensão para o Chrome capaz de gravar um roteiro base.

     Recomendado para capturar os seletores no DOM.

```js
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"


Support Comands


     cypress/support/index.js

```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()
})
```


Login


     cypress/integration/login.spec.js

```js
describe('Conduit Login', () => {
it('Login sucesso', () => {
cy.login('[email protected]', '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('[email protected]', 'senhaerrada')
cy.get('.error-messages > li')
.should('contain', 'email or password is invalid')
})
})
```


Perfil

     cypress/integration/perfil.spec.js

```js
describe('Profile', () => {
it('Editar Perfil', () => {
cy.login('[email protected]', '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()
})
})
```


Feeds

     cypress/integration/feed.spec.js

```js
describe('Conduit Feed', () => {

it('Ver Feeds', () => {
cy.login('[email protected]', '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()
})

})
```


Pagination

     cypress/integration/pagination.spec.js

```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')
})
})
```


Post

     cypress/integration/post.spec.js

```js
describe('Post', () => {

beforeEach(() => {
cy.login('[email protected]', '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')
})

})
```


Tags

     cypress/integration/tags.spec.js

```js
describe('Tags', () => {
it('Adicionar', () => {
cy.login('[email protected]', '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');
})
})
```


Comentários

     cypress/integration/comentarios.spec.js

```js
describe('Comentarios', () => {
it('Escrever', () => {
cy.login('[email protected]', '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!')
})
})
```


Seguir

     cypress/integration/seguir.spec.js

```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()
})
})
```


Logout

     cypress/integration/logout.spec.js

```js
describe('Logout', () => {
it('Logout via Perfil', () => {
cy.login('[email protected]', 'testecypress')
cy.contains('Settings').click()
cy.url().should('include', '/settings')
cy.get('.btn-outline-danger').click()
})
})
```


Fixture: Data-Driven Tests

     cypress/support/index.js

```js
Cypress.Commands.add('loadUsers', () => {
cy.fixture('users')
.as('users')
})
```

     cypress/integration/test.spec.js

```js
// this.users.default.username
// this.users.default.pass
// this.users.client.username
// this.users.client.pass
```

     cypress/fixtures/users.json

```js
{
"default":{
"user": "basic-user",
"pass": "testpass"
},
"client":{
"user": "premium-user",
"pass": "testpass"
}
}
```


     Plugins


     Funcionalidades extendidas


Relatórios e Integrações

     Reports json, html e xml


  • mocha

  • mochawesome

  • mochawesome-merge

  • mochawesome-report-generator

  • cypress-multi-reporters

  • cypress-slack-reporter

  • cypress-sonarqube-reporter


Falso Negativo


     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

Como Identificar?


     Verifique se os as apis e serviços estão rodando:



```js
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)
})

})
```


DevOps

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


Paralelismo


cypress run --parallel --record

Cypress Dashboard


cypress run --record --key=abc123

+ exemplos

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

     Cheatsheet Mocha

     Cheatsheet Chai

     Cheatsheet Cypress