Entenda o que é necessário em testes end-to-end para uma melhor separação de responsabilidades
Ao escrever testes de GUI (interface gráfica do usuário), é uma boa prática separar responsabilidades.
Vejamos um script de teste escrito com o framework Protractor, o qual não separa responsabilidades muito bem, e então explicarei por que isso acontece. Posteriormente, mostrarei como torná-lo melhor usando Page Objects.
// specs/sample.spec.js const helper = require('protractor-helper') describe('Messages sample app', () => { beforeEach(() => browser.get('https://example.com/messages')) it('successfully sends a message', () => { const thankYouHeading = element(by.className('thanks')) sendMessage( 'John', 'johndoe@example.com', 'Hi, this is a test message' ) helper.waitForElementVisibility(thankYouHeading) }) function sendMessage(name, email, message) { const nameField = element(by.id('name')) const emailField = element(by.id('email')) const messageField = element(by.id('message')) const submitButton = element(by.css('button[type="submit"]')) helper.fillFieldWithText(nameField, name) helper.fillFieldWithText(emailField, email) helper.fillFieldWithText(messageField, message) helper.click(submitButton) } })
Os arquivos de teste (*.spec.js
) devem descrever como a aplicação se comporta em diferentes cenários. Olhando para eles, você deve entender:
- Quais são as pré-condições para os testes (ex.:
beforeEach (...)
) - Quais ações cada teste executa (ex.:
sendMessage (...)
) - E por último, mas não menos importante, quais são os resultados esperados para cada cenário (ex.:
helper.waitForElementVisibility(thankYouHeading)
).
Qualquer outra coisa não listada acima é uma distração. Os casos de teste são especificações dos comportamentos da aplicação em diferentes situações. É por isso que os pos-fixamos com .spec
.
Voltando ao exemplo, posso ver pelo menos duas distrações. Elas são:
- As definições dos elementos
- E a função
sendMessage
Mas e se pudéssemos tê-los em um módulo diferente, chamado de página (page
) talvez?
Bem, na verdade nós podemos.
Simplificando, Page Objects são uma abstração usada em automação de testes, onde definimos elementos que poderiam estar presentes em uma página da web, e métodos (que são ações que poderíamos realizar se estivéssemos usando essa página da web).
Um Page Object para o exemplo anterior poderia ser definido da seguinte forma.
// page-objects/sample.js const helper = require('protractor-helper') const nameField = element(by.id('name')) const emailField = element(by.id('email')) const messageField = element(by.id('message')) const submitButton = element(by.css('button[type="submit"]')) const thankYouHeading = element(by.className('thanks')) function sendMessage(name, email, message) { helper.fillFieldWithText(nameField, name) helper.fillFieldWithText(emailField, email) helper.fillFieldWithText(messageField, message) helper.click(submitButton) } module.exports = { nameField, emailField, messageField, submitButton, thankYouHeading, sendMessage }
E o teste pode então importar essa página e usar todos os seus elementos e métodos, conforme necessário (veja abaixo).
// specs/sample.spec.js const helper = require('protractor-helper') const page = require('../page-objects/sample.js') describe('Messages sample app', () => { beforeEach(() => browser.get('https://example.com/messages')) it('successfully sends a message', () => { page.sendMessage( 'John', 'johndoe@example.com', 'Hi, this is a test message' ) helper.waitForElementVisibility(page.thankYouHeading) }) })
Agora, o teste está preocupado exatamente com o que ele deve fazer, testar! Por outro lado, o Page Object trata de localizar elementos e definir formas de interagir com eles (funções).
Dessa forma, agora separamos melhor as responsabilidades e a suite de testes é mais fácil de manter.
Confira o curso de arquitetura de testes com Protractor.
Este conteúdo é uma tradução e foi primeiramente publicado em inglês na Dev Community.