Teste de ponta a ponta com Joomla! e Cypress - Meus primeiros passos e pensamentos

Os testes automatizados não são uma ferramenta especial para desenvolvedores de software em grandes projetos. Especialmente para extensões, os testes automatizados ajudam a identificar rapidamente os problemas. Eles ajudam a garantir que as extensões funcionem sem problemas nas versões mais recentes do Joomla.

Os desenvolvedores Joomla Core querem desenvolvedores de software terceirizados para testar suas extensões, para encontrar bugs antes que um usuário os encontre. Isso requer muito tempo e é um trabalho chato. Portanto, muitas vezes não é feito. Especialmente se tiver que ser feito manualmente por humanos para cada nova versão. O teste automatizado torna possível repetir as etapas manuais para cada versão sem que um humano execute as etapas. Dessa forma, os bugs são encontrados antes que um usuário os encontre ao acessar o sistema ativo.

A propósito, quem quiser conferir o Cypress achará este texto um bom ponto de partida. Você pode testar problemas sem ter que instalar e configurar tudo sozinho. No repositório Github do projeto Joomla está tudo pronto.

Introdução

"Embora seja verdade que a qualidade não pode ser testada, é igualmente evidente que sem testes é impossível desenvolver qualquer coisa de qualidade." – [James A. Whittaker]

Antes de conhecer o Cypress, eu não poderia imaginar que as barreiras que muitas vezes atrapalhavam meus testes seriam realmente afastadas. Passei muito tempo testando software - e antes ainda mais tempo lidando com os problemas que surgiu devido à falta de testes! Agora estou convencido de que os testes que são:

  • o mais próximo possível da programação,
  • automaticamente,
  • frequentemente - idealmente, após cada mudança de programa,

    traga mais do que custa. E mais: o teste pode até ser divertido.

Vale a pena aprender métodos de teste! Os métodos de teste são duradouros, porque podem ser usados ​​não apenas com qualquer linguagem de programação, mas também podem ser aplicados a quase qualquer trabalho humano. Você deve testar quase tudo que é importante na vida de vez em quando. Os métodos de teste são independentes de ferramentas de software específicas.Ao contrário de técnicas de programação ou linguagens de programação, que muitas vezes estão na moda e fora de moda, o conhecimento de como configurar bons testes é atemporal.

Quem deve ler este texto?

Qualquer um que pense que teste de software é uma perda de tempo deveria dar uma olhada neste artigo. Em particular, eu gostaria de convidar a ler isto aqueles desenvolvedores que sempre quiseram escrever testes para seus softwares - mas nunca o fizeram para uma variedade de razões. Cypress poderia ser uma maneira de remover tais barreiras.

alguma teoria

O Triângulo Mágico

O Triângulo Mágico descreve a relação entre os custos, o tempo necessário e a qualidade alcançável. Originalmente, essa relação foi reconhecida e descrita no gerenciamento de projetos. No entanto, você provavelmente já ouviu falar dessa tensão em outras áreas também. questão em quase todas as operações processos em uma empresa.

Por exemplo, geralmente se assume que um custo mais alto tem um impacto positivo na qualidade e/ou na data de conclusão - ou seja, no tempo.

 

O Triângulo Mágico no Gerenciamento de Projetos - Se mais dinheiro é investido no projeto, isso tem um impacto positivo na qualidade ou no tempo.

Por outro lado, uma economia de custos forçará a diminuição da qualidade e/ou o atraso na conclusão.

O Triângulo Mágico em Gerenciamento de Projetos - Se menos dinheiro é investido no projeto, isso tem um impacto negativo na qualidade ou no tempo.

Agora a mágica entra em ação: superamos a correlação entre tempo, custos e qualidade, porque, a longo prazo, isso pode ser realmente superado.

A conexão entre tempo, custos e qualidade pode ser superada no longo prazo.

Talvez você também tenha percebido na prática que uma redução na qualidade não resulta em economia de custos a longo prazo, e a dívida técnica que isso cria muitas vezes leva a aumentos de custos e tempo adicional.

A longo prazo, a correlação entre custo, tempo e qualidade pode realmente ser superada.

Dívida técnica refere-se ao esforço extra envolvido em fazer alterações e aprimoramentos em um software mal programado em comparação com um software bem escrito. Martin Fowler distingue os seguintes tipos de dívida técnica: aquelas contraídas deliberadamente e aquelas contraídas inadvertidamente Ele também distingue entre dívidas técnicas prudentes e imprudentes.

Dívida técnica

Custos e benefícios

Na literatura você encontrará estatísticas devastadoras sobre as chances de sucesso de projetos de software. Pouco mudou no quadro negativo já registrado em um estudo de AW Feyhl na década de 1990. Aqui, em uma análise de 162 projetos em 50 organizações , foi determinado o desvio de custo em relação ao planejamento original: 70% dos projetos apresentaram desvio de custo de pelo menos 50%! Algo não está certo! Você não pode simplesmente aceitar isso, pode?

Uma solução seria dispensar completamente as estimativas de custo e seguir a argumentação do movimento #NoEstimates . Esse movimento é de opinião que as estimativas de custo em um projeto de software não fazem sentido. Um projeto de software contém, segundo a opinião do #NoEstimates, sempre a produção de algo novo. O novo não é comparável com experiências já existentes e, portanto, não é previsível.

Quanto mais experiência eu ganho, mais chego à conclusão de que visões extremas não são boas. A solução quase sempre está no meio. Evite extremos também em projetos de software e procure um meio. 'não tem que ter 100% plano seguro. Mas você também não deve iniciar um novo projeto ingenuamente. Embora o gerenciamento de projetos de software e especialmente a estimativa de custos seja um tópico importante, não vou aborrecê-lo por mais tempo neste texto. O foco deste artigo é mostrar como o E2E o teste pode ser integrado ao fluxo de trabalho prático do desenvolvimento de software.

Integre o teste de software em seu fluxo de trabalho

Você decidiu testar seu software. Ótimo! Qual é o melhor momento para fazer isso? Vamos dar uma olhada no custo de corrigir um bug nas diferentes fases do projeto. Quanto mais cedo você encontrar um bug, menor será o custo de corrigi-lo .

Custos relativos para solução de problemas em vários estágios do projeto

Teste e depuração: há palavras que são frequentemente mencionadas ao mesmo tempo e cujos significados são, portanto, igualados. Em uma inspeção mais detalhada, no entanto, os termos representam interpretações diferentes. Teste e depuração pertencem a essas palavras. Os dois termos têm em comum que detectam avarias, mas também existem diferenças no significado.

  • Os testes encontram defeitos desconhecidos durante o desenvolvimento.Encontrar o mau funcionamento é caro, enquanto a localização e eliminação do erro é barata.
  • Os depuradores corrigem os defeitos encontrados após a conclusão do produto. Encontrar o defeito é gratuito, mas localizar e corrigir o erro é caro.

Conclusão: Faz mais sentido começar a integrar os testes o mais cedo possível, mas infelizmente isso é difícil de implementar em um projeto de código aberto como o Joomla com colaboradores voluntários.

Integração Contínua (CI)
Integração contínua de testes

Imagine o seguinte cenário. Uma nova versão de um popular sistema de gerenciamento de conteúdo está prestes a ser lançada. Tudo o que os desenvolvedores da equipe contribuíram desde o último lançamento agora está sendo usado em conjunto pela primeira vez. trabalho? Todos os testes serão bem-sucedidos - se o projeto integra testes. Ou o lançamento da nova versão terá que ser adiado novamente e horas estressantes de correção de bugs estão por vir? A propósito, adiar a data de lançamento também não é bom para a imagem de o produto de software! Nenhum desenvolvedor gosta de vivenciar este cenário. É muito melhor saber a qualquer momento em que estado o projeto de software está atualmente? caber".Especialmente em tempos em que é cada vez mais comum que uma lacuna de segurança tenha que ser corrigida, um projeto sempre deve ser capaz de criar um release!E é aqui que a integração contínua entra em ação.

Na integração contínua, os elementos individuais do software são permanentemente integrados. O software é criado e testado em pequenos ciclos. Dessa forma, você encontra problemas durante a integração ou testes defeituosos em um estágio inicial e não dias ou semanas depois. Com uma integração bem-sucedida, A solução de problemas é muito mais fácil porque os erros são descobertos perto do tempo de programação e geralmente apenas uma pequena parte do programa é afetada. Joomla integra novo código usando integração contínua. Novo código é integrado somente quando todos os testes são aprovados.

Com uma integração contínua de novos softwares, a solução de problemas é muito mais fácil porque os erros são descobertos próximo ao momento da programação e geralmente apenas uma pequena parte do programa é afetada.

Para garantir que você tenha testes para todas as partes do programa disponíveis o tempo todo durante a integração contínua, você deve desenvolver um software orientado a testes.

Desenvolvimento Orientado a Testes (TDD)

O desenvolvimento orientado a testes é uma técnica de programação que usa o desenvolvimento em pequenas etapas. Primeiro você escreve o código de teste. Só então você cria o código do programa a ser testado. Qualquer alteração no programa é feita somente depois que o código de teste para essa alteração foi foi criado. Portanto, seus testes falham imediatamente após a criação. A função necessária ainda não foi implementada no programa. Somente então você cria o código do programa real - ou seja, o código do programa que satisfaz o teste.

Os testes TDD ajudam você a escrever o programa corretamente .

Quando você ouve pela primeira vez sobre essa técnica, pode não se sentir confortável com o conceito. ""Humano"" sempre quer fazer algo produtivo primeiro, afinal. E escrever testes não parece produtivo à primeira vista. Experimente. Às vezes você se torna amigo de uma nova técnica somente depois de conhecê-la!Em projetos com alta cobertura de teste, me sinto mais confortável quando adiciono novos recursos.

Se você passar pela parte do exercício no final do texto, poderá experimentá-lo. Primeiro crie o teste e depois escreva o código para o Joomla Core. Em seguida, envie tudo junto como um PR no Github . Se todos fizessem isso , Joomla teria cobertura de teste ideal.

Desenvolvimento orientado a comportamento (BDD)

BDD não é outra técnica de programação ou técnica de teste, mas um tipo de melhor prática para desenvolvimento de software. BDD é idealmente usado junto com TDD. Em princípio, Behaviour-Driven-Development significa testar não a implementação do código do programa, mas a execução - ou seja, o comportamento do programa Um teste verifica se a especificação, ou seja, o requisito do cliente, é atendida.

Quando você desenvolve software de maneira orientada ao comportamento, os testes não apenas ajudam você a escrever o programa corretamente, mas também a escrever o programa certo .

O que quero dizer com isso: "Escreva o programa certo"? Acontece que os usuários veem as coisas de maneira diferente dos desenvolvedores. O fluxo de trabalho de exclusão de um artigo no Joomla é um exemplo. Sempre encontro usuários que clicam no ícone de status no lixo e são surpreendidos. O usuário geralmente assume intuitivamente que o item foi excluído permanentemente, mas é alternado de lixo para ativo. Para o desenvolvedor, clicar no ícone de status é uma mudança de status, uma alternância em todas as outras visualizações. Por que isso deveria ser diferente na lixeira? Para o desenvolvedor, a função é implementada sem erros. O Joomla funciona corretamente. Mas, a meu ver, a função não é a correta naquele local, porque a maioria dos usuários a descreveria / solicitaria de maneira bem diferente .

No Behavior Driven Development, os requisitos para o software são descritos através de exemplos chamados cenários ou histórias de usuários.

  • um forte envolvimento do usuário final no processo de desenvolvimento do software,
  • a documentação de todas as fases do projeto com histórias de usuários/exemplos de casos em forma de texto - geralmente na linguagem de descrição na linguagem de descrição Gherkin,
  • teste automático dessas histórias de usuários/estudos de caso,
  • implementação sucessiva. Assim, uma descrição do software a ser implementado pode ser acessada a qualquer momento. Com a ajuda desta descrição, você pode garantir continuamente a correção do código do programa já implementado.

O projeto Joomla introduziu o BDD em um projeto Google Summer of Code . Esperava-se que os usuários sem conhecimento de programação pudessem participar mais facilmente usando o Gherkin ). A abordagem não foi seguida de forma consistente. Naquela época, o Joomla usava o Codeception como um ferramenta de teste.Com o Cypress, o desenvolvimento BDD também é possível desenvolver no modo BDD.

Planejamento

tipos de teste
  • Testes de unidade: Um teste de unidade é um teste que testa as menores unidades do programa independentemente.
  • Testes de integração: Um teste de integração é um teste que testa a interação de unidades individuais.
  • Testes E2E ou Testes de Aceitação: Um teste de aceitação verifica se o programa cumpre a tarefa definida no início.
Estratégias

Se você deseja adicionar uma nova função no Joomla e protegê-la com testes, pode proceder de duas maneiras.

Top-down e bottom-up são duas abordagens fundamentalmente diferentes para entender e apresentar questões complexas. Top-down vai passo a passo do abstrato e geral para o concreto e específico. Para ilustrar isso com um exemplo: Um sistema de gerenciamento de conteúdo como o Joomla geralmente apresenta sites em um navegador.Concretamente, no entanto, há uma série de pequenas subtarefas neste processo.Uma delas é a tarefa de exibir um texto específico em um título.

Bottom-up descreve a direção oposta: neste ponto vale a pena lembrar mais uma vez que um elemento do desenvolvimento dirigido por comportamento é a criação de uma descrição textual do comportamento do software. Esta descrição dos critérios de aceitação ajuda a criar testes - especialmente os principais testes end-to-end ou testes de aceitação.

A abordagem usual para criar testes hoje é de baixo para cima. Se você preferir o desenvolvimento de software orientado a comportamento, você deve usar a estratégia oposta. Você deve usar a estratégia de cima para baixo. Com uma estratégia de cima para baixo, um mal-entendido é identificado desde o início na fase de projeto.

Estratégias de teste: teste de cima para baixo e teste de baixo para cima

  • Teste de cima para baixo: Ao aplicar a estratégia de cima para baixo, começa-se com os testes de aceitação - ou seja, com a parte do sistema que está mais intimamente ligada aos requisitos do usuário. Para software escrito para usuários humanos, geralmente é a interface do usuário . O foco está em testar como um usuário interage com o sistema. Uma desvantagem do teste top-down é que muito tempo deve ser gasto na criação de duplicatas de teste. Componentes que ainda não estão integrados devem ser substituídos por espaços reservados. não há código de programa real no início. Portanto, as partes que faltam devem ser criadas artificialmente. Gradualmente, esses dados artificiais são substituídos por dados realmente calculados.

  • Teste de baixo para cima: se você seguir a estratégia de baixo para cima, comece com testes de unidade. No início, o desenvolvedor tem o estado de destino em mente. No entanto, ele primeiro divide esse destino em componentes individuais. O problema com o A abordagem de baixo para cima é que é difícil testar como um componente será usado posteriormente em situações reais. A vantagem do teste de baixo para cima é que terminamos as partes do software muito rapidamente. No entanto, essas partes devem ser usadas com cuidado. Eles funcionam corretamente. Isso é o que os testes de unidade garantem. Mas se o resultado final é realmente o que o cliente imagina que o software seja, não é garantido.

A pirâmide de teste de Mike Cohn

Quantos testes devem ser implementados de qual tipo de teste? A pirâmide de teste de Mike Cohn descreve um conceito para o emprego dos testes automatizados de software. A pirâmide é composta por três níveis, estruturados de acordo com a frequência de uso e relevância. ‍

Idealmente, a base da pirâmide de testes é formada por muitos testes de unidade rápidos e fáceis de manter, desta forma, a maioria dos erros pode ser detectada rapidamente.

No nível intermediário estão os testes de integração. Eles fornecem serviços para o teste direcionado de interfaces críticas. Os tempos de execução dos testes de integração são mais longos e sua manutenção também é mais complexa do que a dos testes de unidade.

O topo da pirâmide consiste em testes E2E lentos, que às vezes requerem muita manutenção.Os testes E2E são muito úteis para testar o aplicativo como um sistema completo.

Requisitos

Que equipamento você precisa para trabalhar na seguinte parte prática?

Quais requisitos você tem para trabalhar ativamente na parte prática a seguir? Você não precisa atender a muitos requisitos para poder trabalhar no conteúdo deste manual. Claro, você deve ter um computador. Um ambiente de desenvolvimento com Git, NodeJS e Composer e um servidor web local devem ser instalados ou instaláveis ​​nele.

Que conhecimento você deve ter pessoalmente?

Você deve conhecer técnicas básicas de programação. Idealmente, você já programou um pequeno aplicativo da Web. Em qualquer caso, você deve saber onde armazenar arquivos em seu computador de desenvolvimento e como carregá-los em seu navegador de internet.

Experimente. Integre testes em seu próximo projeto. Talvez sua primeira experiência com um teste o salve de uma tediosa sessão de depuração ou de um bug embaraçoso no sistema real. Afinal, com uma rede segura de testes, você pode desenvolver software com menos estresse.

Configurando

Configurando Cypress com Joomla!

Na versão do desenvolvedor disponível no Github, o Joomla está pronto para o Cypress configurado. Já existem testes que você pode usar como guia. Portanto, não é necessário configurar tudo sozinho para obter uma primeira visão geral. Dessa forma, você pode experimentar o Cypress , aprenda sobre suas vantagens e desvantagens e decida por si mesmo se deseja usar a ferramenta de teste.

Etapas para configurar o ambiente local:

Clone o repositório para a raiz do seu servidor web local:

$ git clone https://github.com/joomla/joomla-cms.git

Navegue até a pasta joomla-cms:

$ cd joomla-cms

De acordo com o Joomla Roadmap, a próxima versão principal 5.0 será lançada em outubro de 2023. Para estar atualizado, uso esta versão de desenvolvimento aqui.

Mude para o branch 5.0-dev :

$ git checkout 5.0-dev

Instale todos os pacotes de composição necessários:

$ composer install

Instale todos os pacotes npm necessários:

$ npm install

Para obter mais informações e ajuda sobre como configurar sua estação de trabalho, consulte o artigo de documentação do Joomla "Configurando sua estação de trabalho para desenvolvimento Joomla" . Para Cypress, há informações em cypress.io . Mas isso não é necessário neste momento. Joomla configura tudo para você. Você só precisa configurar seus dados individuais através do arquivo de configuração joomla-cms/cypress.config.js.

Configure seus dados individuais. Para isso, você pode usar o modelo joomla-cms/cypress.config.dist.jscomo orientação. No meu caso, este arquivo se parece com isso:

const { defineConfig } = require('cypress')

module.exports = defineConfig({
  fixturesFolder: 'tests/cypress/fixtures',
  videosFolder: 'tests/cypress/output/videos',
  screenshotsFolder: 'tests/cypress/output/screenshots',
  viewportHeight: 1000,
  viewportWidth: 1200,
  e2e: {
    setupNodeEvents(on, config) {},
    baseUrl: 'http://localhost/joomla-cms',
    specPattern: [
      'tests/cypress/integration/install/*.cy.{js,jsx,ts,tsx}',
      'tests/cypress/integration/administrator/**/*.cy.{js,jsx,ts,tsx}',
      'tests/cypress/integration/module/**/*.cy.{js,jsx,ts,tsx}',
      'tests/cypress/integration/site/**/*.cy.{js,jsx,ts,tsx}'
    ],
    supportFile: 'tests/cypress/support/index.js',
    scrollBehavior: 'center',
    browser: 'firefox',
    screenshotOnRunFailure: true,
    video: false
  },
  env: {
    sitename: 'Joomla CMS Test',
    name: 'admin',
    email: Este endereço de email está protegido contra piratas. Necessita ativar o JavaScript para o visualizar.',
    username: 'admin',
    password: 'adminadminadmin',
    db_type: 'MySQLi',
    db_host: 'mysql',
    db_name: 'test_joomla',
    db_user: 'root',
    db_password: 'root',
    db_prefix: 'j4_',
  },
})

Concretamente, adicionei o diretório tests/cypress/integration/module/**/*.cy.{js,jsx,ts,tsx}ao specPattern Array, porque quero salvar o teste para módulos lá mais tarde. Depois mudei o nome de usuário e as senhas porque também quero testar a instalação manualmente e lembrar melhor dos autoatribuídos. Eu uso um contêiner Docker como banco de dados. Portanto, mudei o servidor de banco de dados e os dados de acesso. E, finalmente, tive que definir a URL raiz http://localhost/joomla-cmsda minha instalação do Joomla.

Usar Cypress

Via navegador

Chame npm run cypress:openvia CLI em seu diretório raiz do Joomla. Pouco tempo depois, o aplicativo Cypress será aberto. Criamos anteriormente o arquivo joomla-cms/cypress.config.dist.js. Que isso foi detectado pode ser visto pelo fato de que o teste E2E é especificado como configurado.

Cypress App abre depois de chamar 96;npm run cypress:open96;.

Aqui você pode escolher se deseja executar os testes E2E e qual navegador deseja usar. Para o exemplo, escolhi a opção "Iniciar testes no Firefox".

Testes E2E no aplicativo Cypress: selecione o navegador a ser usado.

Todos os conjuntos de testes disponíveis serão listados e você pode clicar naquele que deseja executar.Ao selecionar um conjunto de testes, os testes serão executados e você poderá visualizar a execução dos testes em tempo real no navegador.

Conjunto de testes Joomla no Firefox via Cypress App.

Enquanto os testes estão sendo executados, você pode ver o script executado de um lado e o resultado no navegador do lado direito. Não são apenas capturas de tela, mas instantâneos reais do navegador naquele momento, para que você possa ver o código HTML real Também são possíveis capturas de tela e até vídeos dos testes.

Teste de instalação do Joomla em andamento.

Experimente.Se você usar como db_host: 'localhost',você pode testar a instalação e assim ter configurado o Joomla corretamente para o trabalho na parte seguinte deste texto.

Se você, assim como eu, usa uma fonte externa (não lcoalhost; eu uso um container docker) como db_host, o teste para esse tipo de instalação ainda não está pronto. Nesse caso existe uma questão de segurança na rotina de instalação, que é ainda não considerado nos testes. Neste caso, instale o Joomla manualmente com as informações inseridas no arquivo joomla-cms/cypress.config.js. Os testes a seguir usarão as configurações deste arquivo de configuração, por exemplo, para entrar na área de administração do Joomla. Dessa forma, o desenvolvedor do teste faz não precisa se preocupar em inserir os dados de login. O usuário e a senha correspondentes são sempre usados ​​automaticamente a partir do arquivo de configuração.

Sem cabeça

Por padrão, cypress runexecuta todos os testes sem comando . O comando a seguir executa todos os testes já codificados e salva capturas de tela no diretório /joomla-cms/tests/cypress/output/screenshotsem caso de erro. O diretório de saída foi definido no cypress.config.jsarquivo.

$ npm run cypress:run

Outros comandos da CLI

Existem outros comandos úteis que não são implementados como scripts no package.jsonprojeto Joomla. Eu os executo via npx [docs.npmjs.com/commands/npx].

verificação de cipreste

O cypress verifycomando verifica se o Cypress está instalado corretamente e pode ser executado.

$ npx cypress verify

✔  Verified Cypress! /.../.cache/Cypress/12.8.1/Cypress
informações de cipreste

O cypress infocomando gera informações sobre o Cypress e o ambiente atual.

$ npx cypress info
Displaying Cypress info...

Detected 2 browsers installed:

1. Chromium
  - Name: chromium
  - Channel: stable
  - Version: 113.0.5672.126
  - Executable: chromium
  - Profile: /.../snap/chromium/current

2. Firefox
  - Name: firefox
  - Channel: stable
  - Version: 113.0.1
  - Executable: firefox
  - Profile: /.../snap/firefox/current/Cypress/firefox-stable

Note: to run these browsers, pass : to the '--browser' field

Examples:
- cypress run --browser chromium
- cypress run --browser firefox

Learn More: https://on.cypress.io/launching-browsers

Proxy Settings: none detected
Environment Variables: none detected

Application Data: /.../.config/cypress/cy/development
Browser Profiles: /.../.config/cypress/cy/development/browsers
Binary Caches: /.../.cache/Cypress

Cypress Version: 12.8.1 (stable)
System Platform: linux (Ubuntu - 22.04)
System Memory: 4.08 GB free 788 MB
versão cipreste

O cypress versioncomando imprime a versão binária do Cypress instalada, a versão do pacote Cypress, a versão do Electron usada para criar o Cypress e a versão do nó agrupado.

$ npx cypress version
Cypress package version: 12.8.1
Cypress binary version: 12.8.1
Electron version: 21.0.0
Bundled Node version: 16.16.0

A documentação do Cypress fornece informações mais detalhadas.

Escrevendo o primeiro teste próprio

Se tudo funcionou até agora, podemos começar a criar nossos próprios testes.

Obtenha uma visão geral

Aprendendo com testes já desenvolvidos

Na versão de desenvolvimento do Joomla CMS já existem testes do Cypress. Estes estão na pasta /tests/System/integration. Quem gosta de aprender pelo exemplo encontrará aqui uma introdução adequada.

Código de importação para tarefas repetitivas

Os desenvolvedores do Joomla estão trabalhando no projeto NodeJs joomla-cypress , que fornece código de teste para casos de teste comuns. Estes são importados durante a instalação da versão do desenvolvedor do CMS usando npm installvia

  • package.jsone através do
  • arquivo de suporte.O /tests/System/support/index.jsarquivo de suporte é definido na configuração cypress.config.js.
// package.json
{
  "name": "joomla",
  "version": "5.0.0",
  "description": "Joomla CMS",
  "license": "GPL-2.0-or-later",
  "repository": {
    "type": "git",
    "url": "https://github.com/joomla/joomla-cms.git"
  },
...
  "devDependencies": {
    ...
    "joomla-cypress": "^0.0.16",
    ...
  }
}

Um exemplo é o clique em um botão da barra de ferramentas. Por exemplo, Cypress.Commands.add('clickToolbarButton', clickToolbarButton)faz com que o comando clickToolbarButton()fique disponível nos testes customizados e através de cy.clickToolbarButton('new')um clique no botão Newé simulado. O código necessário para isso é mostrado no codenipped abaixo.

// node_modules/joomla-cypress/src/common.js
...
const clickToolbarButton = (button, subselector = null) => {
  cy.log('**Click on a toolbar button**')
  cy.log('Button: ' + button)
  cy.log('Subselector: ' + subselector)

  switch (button.toLowerCase())
  {
    case "new":
      cy.get("#toolbar-new").click()
      break
    case "publish":
      cy.get("#status-group-children-publish").click()
      break
    case "unpublish":
      cy.get("#status-group-children-unpublish").click()
      break
    case "archive":
      cy.get("#status-group-children-archive").click();
      break
    case "check-in":
      cy.get("#status-group-children-checkin").click()
      break
    case "batch":
      cy.get("#status-group-children-batch").click()
      break
    case "rebuild":
      cy.get('#toolbar-refresh button').click()
      break
    case "trash":
      cy.get("#status-group-children-trash").click()
      break
    case "save":
      cy.get("#toolbar-apply").click()
      break
    case "save & close":
      cy.get(".button-save").contains('Save & Close').click()
      break
    case "save & new":
      cy.get("#save-group-children-save-new").click()
      break
    case "cancel":
      cy.get("#toolbar-cancel").click()
      break
    case "options":
      cy.get("#toolbar-options").click()
      break
    case "empty trash":
    case "delete":
      cy.get("#toolbar-delete").click()
      break
    case "feature":
      cy.get("#status-group-children-featured").click()
      break
    case "unfeature":
      cy.get("#status-group-children-unfeatured").click()
      break
    case "action":
      cy.get("#toolbar-status-group").click()
      break
    case "transition":
      cy.get(".button-transition.transition-" + subselector).click()
      break
  }

  cy.log('--Click on a toolbar button--')
}

Cypress.Commands.add('clickToolbarButton', clickToolbarButton)
...

O código a seguir mostra outro exemplo, o login na área de administração.

// /node_modules/joomla-cypress/src/user.js
...
const doAdministratorLogin = (user, password, useSnapshot = true) => {
  cy.log('**Do administrator login**')
  cy.log('User: ' + user)
  cy.log('Password: ' + password)

  cy.visit('administrator/index.php')
  cy.get('#mod-login-username').type(user)
  cy.get('#mod-login-password').type(password)
  cy.get('#btn-login-submit').click()
  cy.get('h1.page-title').should('contain', 'Home Dashboard')

  cy.log('--Do administrator login--')
}

Cypress.Commands.add('doAdministratorLogin', doAdministratorLogin)

...
Tarefas comuns no ambiente individual

No diretório /tests/System/supportvocê encontrará tarefas comuns no ambiente individual. Para que possam ser facilmente reutilizadas, elas são importadas através do arquivo de suporte . Um /tests/System/support/index.jsexemplo de tarefa frequentemente repetida é o login na área de administração, que é tratado no arquivo /tests/System/support/commands.jsusando a função doAdministratorLogin.

O código a seguir também mostra como as informações da cypress.config.jsconfiguração são utilizadas nos testes, Cypress.env('username')é atribuído o valor da usernamepropriedade dentro do grupo env.

Além disso, podemos ver aqui como sobrescrever comandos.sobrescreve Cypress.Commands.overwrite('doAdministratorLogin' ...),o código que acabamos de ver no pacote.A joomla-cypressvantagem é que o usuário e a senha são usados ​​automaticamente a partir da configuração individual.

// /tests/System/support/commands.js
...
Cypress.Commands.overwrite('doAdministratorLogin', (originalFn, username, password, useSnapshot = true) => {
  // Ensure there are valid credentials
  const user = username ?? Cypress.env('username');
  const pw = password ?? Cypress.env('password');

  // Do normal login when no snapshot should be used
  if (!useSnapshot) {
    // Clear the session data
    Cypress.session.clearAllSavedSessions();

    // Call the normal function
    return originalFn(user, pw);
  }

  // Do login through the session
  return cy.session([user, pw, 'back'], () => originalFn(user, pw), { cacheAcrossSpecs: true });
});
...

Instale a própria extensão do Joomla

Para ver como testar seu próprio código, instalaremos um componente de exemplo simples por meio do back-end do Joomla. O arquivo para instalação pode ser baixado do Codeberg .

Instale a própria extensão do Joomla.

Após a instalação, você pode encontrar um link para a exibição do componente Foo na barra lateral esquerda do back-end do Joomla.

Visualização do componente de exemplo no back-end do Joomla.

Agora temos um ambiente de teste configurado e um código para testar.

O primeiro teste próprio

ganchos

Ao testar o back-end, você notará que precisa iniciar cada teste com um login. Podemos evitar esse código redundante usando a beforeEach()função. Esse chamado gancho executa o código que inserimos antes de cada teste ser executado. Daí o nome beforeEach().

O Cypress fornece vários tipos de ganchos , incluindo beforeganchos afterexecutados antes ou depois dos testes em um grupo de teste e beforeEachganchos afterEachexecutados antes ou depois de cada teste individual no grupo. Os ganchos podem ser definidos globalmente ou dentro de um describedbloco específico. O exemplo de código no arquivo tests/System/integration/administrator/components/com_foos/FoosList.cy.jsfaz com que um login seja executado no back-end antes de cada teste dentro do describedbloco test com_foos features.

Agora começamos com a parte prática e criamos o arquivo tests/System/integration/administrator/components/com_foos/FoosList.cy.jscom o próximo codenippted antes de escrever o primeiro teste produtivo. Nosso primeiro exemplo deve nos logar com sucesso no back-end antes de qualquer teste! Vamos testar isso depois de criar o primeiro teste.

// tests/System/integration/administrator/components/com_foos/FoosList.cy.js

describe('Test com_foos features', () => {
  beforeEach(() => {
    cy.doAdministratorLogin()
  })

})

Nota: Os ganchos, que são implementados no arquivo /tests/System/support/index.js, são aplicados a cada arquivo de teste no processo de teste.

Um teste bem sucedido

O componente que instalamos para teste contém os três elementos Astrid, Ninae Elmar. Primeiro, testamos se esses elementos foram criados com sucesso.

// tests/System/integration/administrator/components/com_foos/FoosList.cy.js

describe('Test com_foos features', () => {
  beforeEach(() => {
    cy.doAdministratorLogin()
  })

  it('list view shows items', function () {
    cy.visit('administrator/index.php?option=com_foos')

    cy.get('main').should('contain.text', 'Astrid')
    cy.get('main').should('contain.text', 'Nina')
    cy.get('main').should('contain.text', 'Elmar')

    cy.checkForPhpNoticesOrWarnings()
  })
})

Obs: A função checkForPhpNoticesOrWarnings()você encontra no arquivo /node_modules/joomla-cypress/src/support.js.

Obtemos o elemento DOM mainpor meio do comando Cypress get

Você deve encontrar o teste que acabou de criar FooList.cy.jsna lista de testes disponíveis na barra lateral esquerda. Caso contrário, feche o navegador e execute npm run cypress:opennovamente.

Teste de execução do Joomla para extensão própria.

Clique no nome do teste para executá-lo. Ele deve terminar com sucesso e você verá mensagens verdes.

A visualização após a execução bem-sucedida do teste.

Um teste falhou

Adicione a linha cy.get('main').should('contain.text', 'Sami')ao arquivo de teste para que a execução falhe. Não há elemento com este nome. Depois de salvar o arquivo de teste, o Cypress percebe a alteração. Após cada alteração, o Cypress automaticamente executa novamente todos os testes no arquivo de teste.

// tests/System/integration/administrator/components/com_foos/FoosList.cy.js
describe('Test com_foos features', () => {
  beforeEach(() => {
    cy.doAdministratorLogin()
  })

  it('list view shows items', function () {
    cy.visit('administrator/index.php?option=com_foos')

    cy.get('main').should('contain.text', 'Astrid')
    cy.get('main').should('contain.text', 'Nina')
    cy.get('main').should('contain.text', 'Elmar')
    cy.get('main').should('contain.text', 'Sami')

    cy.checkForPhpNoticesOrWarnings()
  })
})

Como esperado, o teste falha. Há mensagens vermelhas. Você pode ver o código de cada etapa do teste na barra lateral esquerda. Portanto, é possível encontrar o motivo do erro. Para cada etapa, há um instantâneo do documento HTML, para que você possa verificar a marcação a qualquer momento. Isso é útil, especialmente durante o desenvolvimento.

A exibição após o teste falhou.

Execute apenas um teste em um arquivo

Nossa extensão de demonstração contém mais de um layout. Adicione um teste para testar o layout de estado vazio. Como agora temos dois testes neste arquivo, o Cypress sempre executará os dois testes sempre que salvarmos o arquivo. Podemos usar para que apenas um .only()teste É executado:

// tests/System/integration/administrator/components/com_foos/FoosList.cy.js

describe('Test com_foos features', () => {
    beforeEach(() => {
        cy.doAdministratorLogin()
    })

    it('list view shows items', function () {
        cy.visit('administrator/index.php?option=com_foos')

        cy.get('main').should('contain.text', 'Astrid')
        cy.get('main').should('contain.text', 'Nina')
        cy.get('main').should('contain.text', 'Elmar')

        cy.checkForPhpNoticesOrWarnings()
    })

    it.only('emptystate layout', function () {
        cy.visit('administrator/index.php?option=com_foos&view=foos&layout=emptystate')
        cy.get('main').should('contain.text', 'No Foo have been created yet.')
    })
})

Durante o desenvolvimento, isso é muito conveniente.

Atributos especiais de teste

Agora gostamos de testar o front-end do nosso componente. Fazemos isso em um arquivo separado /tests/System/integration/site/components/com_foos/FooItem.cy.js.

Na maioria das vezes, usamos uma classe CSS para obter elementos em testes do Joomla. Embora isso seja perfeitamente válido e funcione, na verdade não é recomendado. Por que não? Quando você usa classes ou IDs CSS, vincula seus testes a coisas que provavelmente mudará com o tempo. Classes e IDs são para design, layout e, às vezes, via JavaScript para controle, o que pode mudar facilmente. Se alguém alterar o nome ou ID de uma classe, seus testes não funcionarão mais. Para tornar seus testes menos frágeis e mais preparado para o futuro, a Cypress recomenda a criação de atributos de dados especiais para seus elementos especificamente para fins de teste.

Vou usar o data-testatributo para os elementos. Primeiro eu adiciono o atributo data-test="foo-main"ao código de produção.

// /components/com_foos/tmpl/foo/default.php

\defined('_JEXEC') or die;
?>
<div data-test="foo-main">
Hello Foos
</div>

Em seguida, testo o código de produção procurando o atributo [data-test="foo-main"].

// tests/System/integration/site/components/com_foos/FooItem.cy.js
describe('Test com_foo frontend', () => {
  it('Show frondend via query in url', function () {
    cy.visit('index.php?option=com_foos&view=foo')

    cy.get('[data-test="foo-main"]').should('contain.text', 'Hello Foos')

    cy.checkForPhpNoticesOrWarnings()
  })
})
Testando um item de menu e algumas ideias sobre eventos, esperas e melhores práticas

Agora gosto de testar a criação de um item de menu para nosso componente. Faço isso em um arquivo separado /tests/System/integration/administrator/components/com_foos/MenuItem.cy.js. Esse código é complexo e apresenta muitos recursos especiais.

Primeiro, defini uma constante na qual defino todas as propriedades relevantes do item de menu. Isso tem a vantagem de, em caso de alteração de uma propriedade relevante, ter que ajustar apenas em um local:

const testMenuItem = {
  'title': 'Test MenuItem',
  'menuitemtype_title': 'COM_FOOS',
  'menuitemtype_entry': 'COM_FOOS_FOO_VIEW_DEFAULT_TITLE'
}

Em seguida, você verá todo o código do arquivo MenuItem.cy.js:

// tests/System/integration/administrator/components/com_foos/MenuItem.cy.js

describe('Test menu item', () => {
  beforeEach(() => {
    cy.doAdministratorLogin(Cypress.env('username'), Cypress.env('password'))
  })

  it('creates a new menu item', function () {
    const testMenuItem = {
      'title': 'Test MenuItem',
      'menuitemtype_title': 'COM_FOOS',
      'menuitemtype_entry': 'COM_FOOS_FOO_VIEW_DEFAULT_TITLE'
    }

    cy.visit('administrator/index.php?option=com_menus&view=item&client_id=0&menutype=mainmenu&layout=edit')
    cy.checkForPhpNoticesOrWarnings()
    cy.get('h1.page-title').should('contain', 'Menus: New Item')

    cy.get('#jform_title').clear().type(testMenuItem.title)

    cy.contains('Select').click()
    cy.get('.iframe').iframe('#collapse1-heading').contains(testMenuItem.menuitemtype_title).click()
    cy.get('.iframe').iframe('#collapse1-heading').contains(testMenuItem.menuitemtype_entry).click()

    cy.intercept('index.php?option=com_menus&view=items&menutype=mainmenu').as('item_list')
    cy.clickToolbarButton('Save & Close')
    cy.wait('@item_list')
    cy.get('#system-message-container').contains('Menu item saved.').should('exist')

    // Frontend
    cy.visit('index.php')
    cy.get('.sidebar-right').contains(testMenuItem.title).click()
    cy.get('[data-test="foo-main"]').should('contain.text', 'Hello Foos')
    cy.checkForPhpNoticesOrWarnings()

    // Trash
    cy.visit('administrator/index.php?option=com_menus&view=items&menutype=mainmenu')
    cy.searchForItem(testMenuItem.title)
    cy.checkAllResults()
    cy.clickToolbarButton('Action')
    cy.intercept('index.php?option=com_menus&view=items&menutype=mainmenu').as('item_trash')
    cy.clickToolbarButton('trash')
    cy.wait('@item_trash')
    cy.get('#system-message-container').contains('Menu item trashed.').should('exist')

    // Delete
    cy.visit('administrator/index.php?option=com_menus&view=items&menutype=mainmenu')
    cy.setFilter('published', 'Trashed')
    cy.searchForItem(testMenuItem.title)
    cy.checkAllResults()
    cy.on("window:confirm", (s) => {
      return true;
    });
    cy.intercept('index.php?option=com_menus&view=items&menutype=mainmenu').as('item_delete')
    cy.clickToolbarButton('empty trash');
    cy.wait('@item_delete')
    cy.get('#system-message-container').contains('Menu item deleted.').should('exist')
  })
})
  • Neste código, você pode ver um exemplo de teste de algo e, em seguida, deletando tudo - restaurando assim o estado inicial. Dessa forma, você pode repetir os testes quantas vezes quiser. Sem restaurar o estado inicial, a segunda execução do teste falhará porque o Joomla não pode armazenar dois elementos semelhantes.

Nota: Um teste deve ser:

  • Repetivel.
  • mantido simples. Em termos concretos, isso significa que ele deve testar um problema limitado e o código para isso não deve ser muito extenso.
  • independente de outros testes.
  • E você pode ver como usar uma rota interceptada definida com cy.intercept()[^docs.cypress.io/api/commands/intercept] como um alias e aguardar a rota definida como um alias com cy.wait().

Ao escrever testes para tais aplicativos, somos tentados a usar valores aleatórios como cy.wait(2000);no cy.waitcomando. O problema com essa abordagem é que, embora isso possa funcionar bem no desenvolvimento. No entanto, não é garantido que sempre funcione. Por quê? Porque o sistema subjacente depende de coisas difíceis de prever, portanto, é sempre melhor definir exatamente o que você está esperando.

  • O código também mostra como aguardar um alerta e confirmá -lo.
cy.on("window:confirm", (s) => {
  return true;
});
  • Por último, mas não menos importante, o código de teste contém Cypress build-in e funções típicas do Joomla que podem ser reutilizadas por desenvolvedores de extensão. Por exemplo, cy.setFilter('published', 'Trashed')ou cy.clickToolbarButton('Save & Close')são funções nas quais soluções para testes individuais podem ser encontradas em geral e que os desenvolvedores Joomla em particular geralmente precisam .
Misturando código assíncrono e sincronizado

Os comandos do Cypress são assíncronos, ou seja, não retornam um valor, mas generatesim. Quando iniciamos o Cypress, ele não executa os comandos imediatamente, mas os lê em série e os enfileira. Se você misturar código assíncrono e síncrono em testes, você pode obter resultados inesperados. Se você executar o código a seguir, receberá um erro contra a expectativa. Certamente você também esperava que isso mainText = $main.text()mudasse o valor mainText. Mas mainText === 'Initial'ainda é válido no final. Por que isso? Cypress primeiro executa o código síncrono em no início e no final. Só então ele chama a parte assíncrona dentro de then(). Ou seja, a variável mainTexté inicializada e logo em seguida é verificada se ela mudou - o que obviamente não é o caso.

let mainText = 'Initial';
cy.visit('administrator/index.php?option=com_foos&view=foos&layout=emptystate')
cy.get("main").then(
  ($main) => (mainText = $main.text())
);

if (mainText === 'Initial') {
  throw new Error(`Der Text hat sich nicht geändert. Er lautet: ${mainText}`);
}

O processamento da fila torna-se bastante claro e visual se observarmos a execução do seguinte código no console do navegador: O texto 'Cypress Test.' aparece bem antes do conteúdo do elemento ser mostrado, embora as linhas de código mainsejam em uma ordem diferente.

cy.get('main').then(function(e){
  console.log(e.text())
})
console.log('Cypress Test.')
Stubs e Espiões

A stubé uma forma de simular o comportamento da função da qual os testes dependem. Em vez de chamar a função real, o stub substitui essa função e retorna um objeto predefinido. Geralmente é usado em testes de unidade, mas também pode ser usado para fins -to-end de teste.

A spyé semelhante ao stub, mas não exatamente o mesmo. Não altera o comportamento de uma função, mas a deixa como está. Captura algumas informações sobre como a função é chamada. Por exemplo, para verificar se a função é chamada com os parâmetros corretos ou para contar quantas vezes a função é chamada.

O exemplo a seguir mostra a spye a stubem ação. Via const stub = cy.stub()criamos o stubelemento e determinamos na próxima etapa que falseé retornado para a primeira chamada e truepara a segunda. Usando cy.on('window:confirm', stub)fazemos o stubser usado para window:confirm'. Na próxima etapa criamos com cy.spy(win, 'confirm').as('winConfirmSpy')o Spyelemento , que observa a chamada de 'window:confirm'. Agora testamos que na primeira chamada a exclusão da categoria é rejeitada e na segunda chamada é confirmada. Ao fazer isso, o garante stubque podemos esperar com certeza quais serão os valores de retorno entregue, 'window:confirm'é encapsulado, @winConfirmSpyajuda a garantir que a função foi realmente chamada - e quantas vezes ela foi chamada.

// tests/System/integration/administrator/components/com_foos/FoosList.cy.js
...
const stub = cy.stub()

stub.onFirstCall().returns(false)
stub.onSecondCall().returns(true)

cy.on('window:confirm', stub)

cy.window().then(win => {
  cy.spy(win, 'confirm').as('winConfirmSpy')
})

cy.intercept('index.php?option=com_categories&view=categories&extension=com_foos').as('cat_delete')
cy.clickToolbarButton('empty trash');

cy.get('@winConfirmSpy').should('be.calledOnce')
cy.get('main').should('contain.text', testFoo.category)


cy.clickToolbarButton('empty trash');
cy.wait('@cat_delete')

cy.get('@winConfirmSpy').should('be.calledTwice')

cy.get('#system-message-container').contains('Category deleted.').should('exist')
...

Se for apenas uma questão de definir um valor fixo para a 'window:confirm'chamada, o código a seguir fará o trabalho.

cy.on("window:confirm", (s) => {
  return true;
});

conclusão

Neste artigo, você viu a teoria básica e os recursos práticos do teste E2E com Cypress. Usei a instalação do Joomla para demonstrar como escrever diferentes testes para garantir que um componente Joomla em um site funcione conforme o esperado. Também mostrei como personalizar o Cypress Test Runner no arquivo cypress.json e como usar comandos personalizados do Cypress. Isso foi feito usando exemplos fáceis de seguir.

Espero que tenha gostado do passeio pelo Cypress usando o Joomla como exemplo e que tenha conseguido levar muito conhecimento e inspiração para si mesmo.

An online collaborative community manual for Joomla! users, developers or anyone interested in learning more about Joomla! Currently, we have more than 9,000 articles written, maintained, and translated by our Joomla! community members. 

Observe que este site usa um sistema de tradução automática para ajudar na tradução para os diferentes idiomas. Pedimos desculpas por qualquer erro ou erro de digitação que possa ser mostrado nos diferentes textos.