Uma vez que você determinou que está no negócio dos testes automatizados no navegador, está com seu ambiente Playwright pronto para começar a escrever os testes, você vai geralmente executar uma combinação de três passos:
- Configurar os dados
- Executar um conjunto discreto de ações
- Avaliar os resultados
E você deverá manter esses passos os mais curtos possível; uma ou duas operações devem ser suficientes maioria das vezes.
Arrange, Act and Assert (AAA) pattern
Ref: Paulo Gomes: Unit Testing and the Arrange, Act and Assert (AAA) Pattern
O padrão AAA (Arrange-Act-Assert) tornou-se quase um padrão em todo o setor. Ele sugere que você divida seu método de teste em três seções: organizar, agir e afirmar. Cada um deles é responsável apenas pela parte em que recebe o nome.
Portanto, na seção de organização (Arrange), você só tem o código necessário para configurar esse teste específico. Aqui, os objetos seriam criados, a configuração dos mocks (se você estiver usando um) e as expectativas potencialmente definidas. Depois, há a Ação (Act), que deve ser a invocação do método que está sendo testado. E no Assert você simplesmente verificaria se as expectativas foram atendidas.
Seguir esse padrão torna o código bem estruturado e fácil de entender. Em linhas gerais, ficaria assim:
// arrange
const homePage = new HomePage(page, BASE_URL);
// act
await homePage.navigate();
// assert
await expect(homePage.title).toBeVisible();
Exemplo
Larry criou um site que permite que os usuários comprem unicórnios personalizados. O fluxo geral ("caminho feliz") seria algo semelhante a isso:
- Criar uma conta
- Configurar o unicórnio
- Adicionar ao carrinho de compras
- Verificar e pagar
- Dar feedback sobre o unicórnio
Em vez de um roteiro de teste que envolva todas as etapas, separar em testes independentes e rápidos.
Se precisarmos testar apenas o "configurar o unicórnio", vamos precisar da primeira etapa "criar uma conta", mas não estamos testando a criação de conta.
Criar a conta (independente se vai usar uma conta existente ou criar uma nova ou criar uma conta de administrador...) deve fazer parte do "configurar os dados".
A fase de configuração (arrange) pode ser incluída com apenas uma ou duas linhas de código:
/*
Crie um usuário que tenha permissões somente leitura.
eles podem configurar um unicórnio, mas eles não têm
informações de pagamento configuradas,
nem têm privilégios administrativos
No momento em que o usuário é criado, seu endereço
de e-mail e senha são gerados aleatoriamente
você nem precisa conhecê-los.
*/
let user = await userFactory.createCommonUser ();
// Esse método foi criado em outro lugar
/*
Faça login como este usuário.
O login neste site leva você à sua página pessoal
"Minha conta", e então o objeto AccountPage
é retornado pelo método loginAs, permitindo que
você execute ações da AccountPage.
*/
let accountPage = await loginAs(user.email, user.password);
A userFactory pode ser estendida para criar usuários administrativos ou outros tipos, retornando dados necessários para uso, como login, nome, e-mail, senha...
Estamos usando o padrão Page Object Model (POM) neste caso.
A utilização desses métodos, fora do teste de "configurar unicórnio" não deve atrapalhar/distrair o teste em si.
Os testes devem ser compostos de ações, realizadas do ponto de vista do usuário, dentro do contexto das páginas do site. Essas páginas são armazenadas como objetos com informações específicas sobre como a página é composta e como as ações são realizadas.
O testador deve se preocupar com o objetivo do teste: que tipo de unicórnio? Rosa ou roxo? Com óculos escuros? Tatuagem?
E o teste ainda não precisa pensar em botões, campos, menus, formulários. Escrever o código como o usuário tentando resolver o problema. Fase de ação (executa rum conjunto discreto de ações):
/*
O Unicórnio é um objeto de nível superior - ele possui atributos,
que são definidos aqui. Isso armazena apenas os valores;
não preenche formulários da web nem interage com o navegador
de qualquer forma.
*/
let sparkles = new Unicorn(
"Sparkles",
UnicornColors.PURPLE,
UnicornAccessories.SUNGLASSES,
UnicornAdornments.STAR_TATTOOS);
/*
Uma vez que já estamos "na" página da conta,
temos que usá-la para chegar ao lugar real onde
você configura os unicórnios. Chamar o método
"Add Unicorn" nos leva lá.
*/
let addUnicornPage = await accountPage.addUnicorn();
/*
Agora que estamos na AddUnicornPage, passaremos o objeto
"sparkles" para o método createUnicorn().
Este método pegará os atributos do Sparkles,
preencher o formulário e clicar em enviar.
*/
let unicornConfirmationPage = await addUnicornPage.createUnicorn(sparkles);
Agora com o unicórnio configurado, etapa 3 (assert): certificar-se de que funcionou:
/*
O método exists() de UnicornConfirmationPage
pegará o objeto Sparkles - uma especificação
dos atributos que você deseja ver e compará-los
com os campos na página
*/
await expect(unicornConfirmationPage(sparkles)).toBeVisible();
Sem botões, localizadores, controles de navegador. Este método de modelagem mantém os testes imutáveis. Se a lógica por trás disso alterar (posição dos botões, uso de framework diferente), o teste continua válido.
Os objetos de página é que precisarão de alterações nesses casos de redesenho do site, mas os testes permanecerão os mesmos.
Page Object Model (POM)
Ref: Selenium: Page Object Models
Ref: Danilo José: POM (Page Object Model design pattern)
Ref: Playwright: Page Object Models
De acordo com o padrão Page Object Model, devemos manter nossos testes e localizadores de elementos separados, usando um conceito de código limpo para facilitar o entendimento e a manutenção.
Em geral, os casos de teste são otimizados, pois podem usar métodos de objeto de página em classes POM. Isso significa que quaisquer mudanças na interface do usuário podem ser facilmente implementadas, atualizadas ou mantidas dentro dos objetos de página e classes.
Outra característica importante é que há uma separação clara entre o código de teste e o código específico da página, como localizadores e layout, tornando o código muito mais fácil de manter. Em outras palavras, isso faz com que alterações de código sejam necessárias apenas nas classes de objeto de página quando houver uma alteração na interface do usuário, conforme mencionado anteriormente, reduzindo também a duplicação de código.
Um objeto de página é uma classe orientada a objeto que serve como uma interface para uma página de teste de automação. Os testes usam os métodos dessa classe de objeto de página sempre que precisam interagir com a interface do usuário dessa página.
Outra vantagem é que há um único repositório para os serviços de operações oferecidos pela página, em vez de ter esses serviços espalhados pelos testes (teste de login aponta para o cabeçalho da home, teste de logout aponta para o cabeçalho da home, teste de navegação aponta para o cabeçalho na home...). Quaisquer alterações necessárias devido a alterações na interface do usuário são feitas em um só lugar.
Implementação (com Playwright)
Vamos criar uma classe HomePage para encapsular elementos e operações comuns da homepage do projeto:
// page-objects/HomePage.ts
import { Page, Locator } from '@playwright/test';
export default class HomePage {
readonly page: Page;
readonly baseUrl: string;
readonly mainContent: Locator;
readonly cardContact: Locator;
constructor(page: Page, baseUrl: string) {
this.page = page;
this.baseUrl = baseUrl;
this.mainContent = page.locator('data-test=main-content');
this.cardContact = page.locator('data-test=card-contact');
}
async navigate(): Promise<void>{
const url = await this.page.url();
if (url != this.baseUrl) {
await this.page.goto(this.baseUrl);
}
await this.page.waitForLoadState('networkidle');
}
}
Agora importamos a classe HomePage no nosso teste:
// HomePageExample.spec.ts
import { expect, test} from '@playwright/test';
import { HomePage } from './page-objects/HomePage';
test('Home Page should be successfully loaded', async ({page}) => {
cont BASE_URL = 'https://meuprojeto.com.br';
const homePage = new HomePage(page, BASE_URL);
// using HomePage class method to navigate
await homePage.navigate();
const pageTitle = await page.title();
await expect(pageTitle).toBe('Meu Projeto');
await expect(page.url()).toBe(BASE_URL);
// using HomePage class locators
await expect(homePage.mainContent).toBeVisible();
await expect(homePage.cardContact).toBeVisible();
});
Se outros testes precisarem usar elementos da Homepage, eles podem chamar a classe HomePage e usar seus métodos e localizadores.
Se a interface do usuário for alterada, o testador só precisa alterar os localizadores na classe auxiliar e não em várias linhas em vários testes diferentes.
Regras para objetos de página
Os próprios objetos de página nunca devem fazer asserções, verificações ou afirmações. Isso é parte do teste e deve estar no código de teste. O objeto de página conterá a representação da página, os serviços que a página fornece por meio de métodos. Nenhum código que está sendo testado deve estar dentro do objeto de página.
Evitar coisas como:
await homePage.verifySearchIsVisible();
...deixando a asserção com o método verifySearchIsVisible() do objeto de página homePage.
Idealmente a homePage pode retornar o Search input e o teste faz a asserção se ele está visível:
await expect(homePage.searchInput).toBeVisible();
Se a entrada de pesquisa for alterada, o testador precisará alterar apenas a classe que manipula a entrada e não todos os testes que usam essa classe.
Há uma única verificação que pode e deve ser feita dentro do Objeto de Página: verificar se a página, e possivelmente elementos críticos da página, foram carregados corretamente. Esta verificação deve ser fei enquanto instanciar o objeto de página.
Um objeto de página não precisa necessariamente representar todas as partes da própria página. Os mesmos princípios de objeto de página devem ser usados para "objetos de componente de página", que representam partes da página que podem ser incluídas em objetos de página ou testes.
Um objeto de página pode incluir vários objetos de componente, e cada objeto de componente de página tem seus próprios métodos para utilizar a funcionalidade a que serve, como uma barra de navegação, uma caixa de pesquisa, um cabeçalho ou rodapé (aninhando objetos de componente dentro de outros objetos de componente para mais páginas complexas ou a objetos que são usados em todo o site), melhorando a manutenção e reduzindo a duplicação de código.
Top comments (0)