Playwright e Page Object Model (POM)
Também é muito comum perguntarem: Pode usar Page Object Model – POM com Playwright?
Esta pergunta vem normalmente da comparação com o Cypress onde a documentação oficial não recomenda usar Page Objects. Para o caso do Cypress, eu recomendo assistir a playlist de Page Objects do Walmyr onde ele discute bem esse tema.
E para o Playwright a resposta é: Depende (minha resposta favorita!). Você pode usar Page Objects no Playwright, assim como em qualquer outro framework (inclusive no Cypress!!).
Nenhum framework, na verdade, impede o uso de POMs. É preciso conhecer e entender as boas práticas de cada um para que você explore o melhor uso das funcionalidades que ele oferece. O que não é recomendado é você utilizar um novo framework igualzinho um framework que você tenha utilizado no passado sem considerar suas boas práticas. Pois cada framework tem uma forma de funcionamento única. Pode até apresentar semelhanças, mas ninguém faz um framework pra ser igual a outro.
Em resumo, o Playwright não se posiciona a favor ou contra o uso de POMs, então você deve avaliar se faz sentido para o seu projeto.
Origens do Page Object Model (ou POM)
É difícil afirmar com certeza quando surgiu, mas um dos primeiros registros foi por volta de 2006 por Simon Steward – que é o criador do webdriver e líder técnico do Selenium (de acordo com https://bard.google.com/, John Ferguson Smart, e próprio site do Simon).
Bom, estamos em 2023, este conceito surgiu por volta de 2006 (são 17 anos!!!). Então te convido a pensar comigo: Como nós desenvolvíamos software naquela época?
Parece que tem pouco tempo, mas o primeiro iPhone foi lançado em 2007, a primeira versão do ReactJS foi lançada em 2013, a primeira versão do Cypress é de 2017 e a do Playwright é de 2020, e por aí vai. Então, desenvolver software em 2006 com certeza era muito diferente de como era hoje e, consequentemente, testar software também.
Apesar de ser um conceito extremamente importante para a área de arquitetura de testes, é importante fazermos as adaptações e atualizações necessárias para acompanhar a evolução tecnológica.
O que é Page Object Model (ou POM)
Em sua proposta inicial, de 2006, a ideia era oferecer uma forma de escrever testes mais próxima de Orientação a Objetos para facilitar a manutenção do código.
Sem o POM, os locators (que são os elementos de uma página) e seus métodos, ficariam no meio do código de testes e isso gerava muito trabalho para atualizar ou identificar falhas. Com o POM, cada classe teria seus locators mapeados e também os seus respectivos métodos, e os arquivos de testes ficariam mais limpos e menos acoplados. Para cada página de uma aplicação, seria necessário um arquivo com todos os campos/locatos daquela página e os métodos para interagir com estes locators (digitar, clicar, etc). Existem alguns outros formatos de arquivo que também são considerados POMs; comentei sobre eles na sessão “Contribuições da Comunidade” mais abaixo.
De forma simplificada, estes seriam os benefícios do POM (ainda naquela época):
- facilidade de manutenção: já que os locators e métodos ficariam todos em um único arquivo (chamado “pom file” ou “arquivo pom”), e o teste em um arquivo separado.
- redução de duplicidade de código: já que caso um teste precisasse utilizar um locator, ao invés de reescrevê-lo, seria necessário apenas importar a classe em que o locator está mapeado (no pom file).
- facilidade de leitura de código: com esta separação é mais fácil saber onde ir para entender onde um locator está mapeado e entender os métodos relacionados a ele.
Nos dias de hoje, em 2023, a forma de interagir com elementos de uma página nos frameworks (como Playwright, Cypress e o próprio Selenium) evoluiu bastante. E é claro a forma com o que as aplicações são desenvolvidas ditam como será a automação.
Existem aplicações onde uma parte grande do fluxo é feito em apenas uma página. Por exemplo no site www.gap.com. Após um item ser adicionado ao carrinho e ser feito o sign in, na página de checkout podemos observar que tudo é carregado numa mesma página (endereço de entrega, forma de envio, forma de pagamento), muda só o que é exibido, e possui inúmeros fluxos. Para este caso, se formos seguir o conceito de 2006, todos os campos/locators e seus métodos seriam mapeados em apenas 1 arquivo fazendo com que o arquivo ficasse muito grande invalidando o propósito inicial.
Nos dias de hoje, então, para continuarmos a usurfruir dos benefícios do conceito de POM, temos que abstrair a ideia inicial. Neste exemplo, é como se fossem várias páginas dentro de uma. Podemos ver:
- cabeçalho (com o link “Back to Bag”, os links de outros sitesm e o link “Give Us Feedback”)
- o container/bloco “EMAIL ADDRESS”
- o container/bloco “SHIPPING ADDRESS”
- o container/bloco “PAYMENT METHOD”
- o container/bloco “ORDER SUMMARY”
Neste caso, podemos por exemplo, considerar cada um desses blocos como um POM file. E ainda, se houver muita complexidade em um dos blocos, podemos também subdividi-lo. Tudo “depende” do que precisa ser testado. A ideia é preservar a facilidade de manutenção, reduzir a duplicidade, e preservar a facilidade de leitura.
Em resumo, Page Object Model ou POM, é uma representação de uma página (pelo conceito de 2006) ou um bloco/componente/contexto (em um conceito mais moderdo) com seus locators e métodos de interação. Este é um conceito subjetivo, então é importante alinhar com o time como será aplicado o conceito dentro do seu projeto. Talvez um nome mais interessante para a abordagem moderna seja “Component Object Model” ou “Context Object Model”…. vamos abirr uma votação rsrs.
Como usar Page Object Model (ou POM) com Playwright
Esse é um assunto extenso. Como comentei no tópico anterior, o conceito de Page Object Model depende do contexto. E aqui vamos ver exemplos de como utilizá-lo no conceito mais moderno.
Exemplo 1:
- Contexto: Este código foi implementado para uma batalha de código, logo não existe cenário “real” de time, demandas, etc então eu segui os princípios que eu acredito serem melhores para um contexto genérico – o projeto e as pessoas sempre devem ser considerados para uma decisão formal. Os testes foram implementados com e sem Page Object Model, como exercício, para avaliar as duas opções. Os desafios da batalha eram:
- Testar o caminho feliz da funcionalidade de login (As credenciais de acesso (usuário e senha) devem ser protegidas, visto que, na “vida real”, tais dados são sensíveis, além de variarem de ambiente-para-ambiente)
- Testar o caminho feliz da funcionalidade de checkout (Para tornar o teste mais “vida real”, o usuário deve adicionar e remover items do carrinho, antes de completar o processo de checkout)
- Testar a funcionalidade de ordenação de produtos (Sinta-se a vontade para testar uma ou mais funcionalidades de ordenação (ex.: Name A-Z, Name Z-A, Price Low-High, Price High-Low))
- Testar os links de rodapé (redes sociais) (Deve-se verificar que os links (corretos) abrem em uma nova aba)
- Configurar os testes para execução em um pipeline de integração contínua em ao menos 3 navegadores diferentes.
- Teste Login (desafio 1) sem POM:
Os locators (getByTestId, locator, getByRole, etc) não foram escolhidos com intenção de performance, e sim de demonstrar a variedade de possiblidades. Recomendo assistir o webinar “Locator strategies with Playwright” para mais detalhes sobre locators.
- Teste Login (desafio 1) com POM:
1 POM file foi necessário para este teste: LoginPage
- Considerações Teste Login:
Para o cenário sem o POM, o teste não é tão complexo de ser entendido pois é uma funcionalidade muito direta com apenas 3 campos basicamente. Eu acabei fazendo um métod `doLogin` pra evitar código repetido; eu não recomendo o uso de métodos no spec file, prefiro mantê-lo o mais limpo possível – se em algum outro arquivo a gente precisar de fazer um login, esse métido estaria técnicamente no lugar errado. O Playwright oferece uma funcionalidade que armazena e carrega o login automaticamente nos testes então o Page Object neste caso traria mais uma vantagem pois basicamente os mesmos passos seriam usados no arquivo de setup permitindo o reuso do código nestes dois lugares (setup e spec). Por mais simples que este cenário seja, eu recomendo o uso de POM.
- Teste Checkout (desafio 2) sem POM:
Os locators (getByTestId, locator, getByRole, etc) não foram escolhidos com intenção de performance, e sim de demonstrar a variedade de possiblidades. Recomendo assistir o webinar “Locator strategies with Playwright” para mais detalhes sobre locators.
- Teste Checkout (desafio 2) com POM:
6 pom files foram necessários para este teste:
- Considerações Teste Checkout:
Tem várias formas de analisar essas 2 abordagens. Vamos lá:
No cenário sem pom, o arquivo tem mais linhas de código, e ao ler o documento, você precisar de prestar um pouco mais de atenção para entender o que está sendo feito. O Playwright oferece “test.step” que deixa o código um pouco mais organizado, porém ainda sim ao ler o código abaixo
await test.step('go to cart', async () => {
await page.locator('#shopping_cart_container').filter( { hasText: '2' }).click();
await expect(page.getByRole('button', { name: 'Remove' })).toHaveCount(2);
});
você precisa pensar um pouco e meio que “adivinhar” o que está sendo feito. Enquanto que o mesmo trecho de código usando pom é bem mais sugestivo:
await headerPage.goToCart(productData.items);
await cartPage.checkItemsInCart(productData.items);
Claro que você ainda precisa ir no pom file, entender o que está sendo feito, e se necessário fazer algum ajuste. Mas imagine que você é novo no projeto, e é informado que tem uma mudança na parte “checkoutStepTwoPage”. Ao invés de ler o arquivo `checkout.spec.ts` inteiro e entender onde é que o código será impactado, você pode ir no arquivo `checkout-pom.spec.ts` e entender que você precisará entender apenas esses 2 métodos (pois eles claramente mencionam “checkoutStepTwoPage” ):
await checkoutStepTwoPage.checkOrderInfo(orderInfo);
await checkoutStepTwoPage.completeCheckout();
Além disso, se esta funcionalidade estiver sendo usado em mais lugares do código, você precisará, sem pom, também procurar no código e dar a sorte de achar todos os lugares que a utilizam (ainda precisará considerar que os locators podem estar mapeados de forma diferente). Enquanto que com pom, você altera somente no pom e se tiver impacto na lógica, você pode facilmente buscar pelo nome do método e avaliar se precisa de ajuste ou não.
Uma outra análise é sobre o ponto de vista de implementar novos cenários, sem o pom, você precisará ir ao sistema, investigar qual locator utlilizar, ou usar a ferramenta de gravação, copiar, colar, rodar o teste, etc. Enquanto que com pom você pode simplesmente digitar a linha de códgio equivalente a funcionalidade que você precisa. No caso de você ainda não conhecer o sistema, como existe um padrão, você irá a um arquivo único (o pom) e facilmente identificará o que precisa. Ao invés de ter que usar ferramentas de busca no código por palavras-chave ou envolver outros desenvolvedores para explicar o funcionamento.
Exemplo 2:
- Site: https://demoqa.com/books
- Contexto: Este código foi implementado para o curso avançado de Playwright. Como foram implementados cenários específicos para demonstração de certas funcionalidades, algumas boas práticas não foram adotadas. Mais uma vez, este código não apresenta cenários reais de um projeto. Neste exemplo, temos um cenário um pouco diferente do exemplo 1. Em todos os spec files eu usei POM, e em 2 arquivos eu fiz o uso de Page Object Model de forma dinâmica. Esse conceito nada mais é que o agrupamento de funções ou passos semelhantes vinculados a criação de um Page Object como por exemplo o “page.goto” (função para carregar uma URL). Neste caso, você pode inserir no arquivo `hooks.ts` os comandos que desejar e, ao invés de criar cada page object no spec file, ele é criado adentro do próprio
hooks
quando ele é chamado. Caso você tenha certas funções que não serão sempre executadas, você pode colocar condicionais no código (ifs); cuidado para não deixar o código muito complexo.
Os POMs foram criados da mesma forma que nos exemplos anteriores.
- Considerações: No segundo arquivo as linhas comentadas 9 e 10 demonstram como seria no formato normal de Page Objects. Neste caso, a linha 11 irá fazer a criação do objeto Profile page e seguir com o teste. Como a intenção dos testes não era exercitar vários cenários, fica difícil comparar com e sem POM neste exemplo, mas podemos ver que mais uma vez as instruções são simples e claras em ambos os cenários.
Quando usar Page Object Model (ou POM) com Playwright
É importante alinhar com seu time qual o padrão que irão usar (se vai ter Page Object Model para todas as páginas/blocos/componentes) ou se não vai ter Page Object Model de forma alguma. Não é recomendado ter para alguns cenários e não ter para outros. Em minha experiência com Playwright, ainda não tive situação onde POMs não valessem a pena. Então, após avaliar seu projeto e ver que realmente faz sentido, determinem alguns direcionamentos para criação deles. Aqui alguns pontos que eu geralmente avalio.
- grupos de elementos na página que são relacionados ao mesmo tema: cabeçalho, menus, rodapé, blocos de elementos que se repetem em várias páginas (por exemplo, ícone de carrinho num site de compras), buscas, filtros, ordenação: É importante desacoplar para manter o código limpo, mas avalie também se não está granulado demais gerando quantidade excessiva de arquivos.
- quantidade de fluxos que irão utilizar os elementos e métodos relacionados a eles: Se tem muitos fluxos que utilizam os elementos, é interessante ter um POM file exclusivo para evitar duplicidade de código. Avalie também, se tiver muitos métodos, se é possível dividir o arquivo ainda mais para evitar que fique muito grande. Ou passar métodos agrupados para outra camada; neste caso é importante avaliar MUITO BEM para não gerar complexidade de código.
- quantidade de pessoas trabalhando no projeto: Imagine a quantidade de mudanças que um arquivo sofrerá em uma sprint ou fase do seu projeto; você também deve avaliar possíveis conflito de código e facilidade de integração com o repositório de controle de versão.
Contribuições da Comunidade
Conversando com a galera do grupo de whatsApp Playwright Brasil, surgiram algumas ideias de como usar POM:
- mapear os locators em um json, yml, ou outro tipo de arquivo, sem os métodos: importante avaliar aqui os pontos que mencionei no “Quando usar Page Object Model (ou POM) com Playwright”, se o time for muito grande e tiver muita manutenção neste arquivo, pode ser que comece a gerar muito conflito de código. Se o arquivo ficar muito grande também pode não ser interessante. Definir um padrão de como será esse arquivo também é importante: será ordenado alfabeticamente ou de outra forma? assim você facilita a manutenção
- mapear os locators nos arquivos “actions”: uma abordagem interessante. Esse conceito de “actions” vem do Java; se você estiver usando JavaScript/TypeScript, esse conceito talvez não seja tão familiar. Eu sempre acho importante usar conceitos da tecnologia que estivermos desenvolvendo, então apesar de “actions” ser muito semelhante ao conceito de “pom file”, se for para JavaScript/TypeScript talvez seja interessante usar outra abordagem.
- usar PageBase: uma abordagem que surgiu de algumas limitações do Selenium. Mais uma vez, recomendo entender bem o framework que está utilizando e quais as vantagens/inovações que ele oferece ao invés de seguir padrões que estamos acostumados. Não é errado usar PageBase, mas é importante usar técnicas de forma consciente.
- usar singleton + POM: também um design pattern recomendado para linguagens orientadas a objetos. Para JavaScript/TypeScript é um anti-pattern.
- usando
export const
+variavel: locator
: uma abordagem também interessante. Bem mais simples do que o “original POM do Playwright“. É importante avaliar aqui a diferença de performance e possíveis problemas entre o uso de “export const” versus “function” no JavaScript/TypeScript. Uma vez compreendido, use o que for mais relevante para seu projeto. - usar uma camada “abaixo” do Page object, que pode ser chamada de “Component Object”: para aplicações que tem componentes sendo reusados em mais de uma página, essa abordagem é muito interessante. No meu ponto de vista, ainda se encaixa no “conceito moderno” de Page Object, mas de fato, o mapeamento não é por “página” e sim por “contexto” ou “componente”.
Obrigada a todos que contribuiram: Felipe, Fábio dos Anjos, Wallace Petrucci, Marcos, Jess, Josias Valentim, Reinaldo Rossetti! Vocês são show!!
Conclusão
Dependendo do framework e do tamanho do projeto, Page Object Model pode ser muito interessante (lembre-se de abstrair os conceitos e aplicar soluções mais modernas e eficientes de acordo com a linguagem e o framework que você está usando). Ressalto que, cada caso deve ser avaliado individualmente e as orientações sobre uso deve ser acordada e padronizada entre o time.
Muitas vezes não é possível avaliar todos os cenários no início do projeto. Lembre-se também de se manter aberta(o) para possíveis refactorings que possam ser necessários e de reavaliar as decisões de tempos em tempos para manter seu código saudável.
Referências Complementares
- Singleton: https://blog.bitsrc.io/the-singleton-pattern-in-typescript-b906303fda93
- export const vs function: https://stackoverflow.com/questions/39582318/export-const-arrow-function-or-basic-function
- export const vs function: https://dev.to/skinnypetethegiraffe/function-or-const-which-do-you-prefer-typescriptjavascript-apa
- Locator Strategies with Playwright: https://testingwithrenata.com/portfolio/applitools-webinar-locator-strategies-playwright/
- Cursos de Playwright: https://testingwithrenata.com/trainings/
- Vídeo no canal do YouTube: @TestingWithRenata
1 thought on “Playwright e Page Object Model (POM)”