Testando modelos HTML


foo

Vamos ver como fazer isso em etapas: começamos com o seguinte teste que tenta compilar o modelo. Em Go usamos o padrão html/template pacote.

Ir

  func Test_wellFormedHtml(t *testing.T) {
    templ := template.Should(template.ParseFiles("index.tmpl"))
    _ = templ
  }

Em Java, usamos j bigode
porque é muito simples de usar; Marcador livre ou
Velocidade são outras escolhas comuns.

Java

  @Check
  void indexIsSoundHtml() {
      var template = Mustache.compiler().compile(
              new InputStreamReader(
                      getClass().getResourceAsStream("/index.tmpl")));
  }

Se executarmos este teste, ele falhará, porque o index.tmpl arquivo não existe. Então nós o criamos, com o HTML quebrado acima. Agora o teste deve passar.

Então criamos um modelo para o modelo usar. O aplicativo gerencia uma lista de tarefas, e podemos criar um modelo mínimo para fins de demonstração.

Ir

  func Test_wellFormedHtml(t *testing.T) {
    templ := template.Should(template.ParseFiles("index.tmpl"))
    mannequin := todo.NewList()
    _ = templ
    _ = mannequin
  }

Java

  @Check
  void indexIsSoundHtml() {
      var template = Mustache.compiler().compile(
              new InputStreamReader(
                      getClass().getResourceAsStream("/index.tmpl")));
      var mannequin = new TodoList();
  }

Agora renderizamos o modelo, salvando os resultados em um buffer de bytes (Go) ou como um String (Java).

Ir

  func Test_wellFormedHtml(t *testing.T) {
    templ := template.Should(template.ParseFiles("index.tmpl"))
    mannequin := todo.NewList()
    var buf bytes.Buffer
    err := templ.Execute(&buf, mannequin)
    if err != nil {
      panic(err)
    }
  }

Java

  @Check
  void indexIsSoundHtml() {
      var template = Mustache.compiler().compile(
              new InputStreamReader(
                      getClass().getResourceAsStream("/index.tmpl")));
      var mannequin = new TodoList();
  
      var html = template.execute(mannequin);
  }

Neste ponto, queremos analisar o HTML e esperamos ver um erro, porque em nosso HTML quebrado há um div elemento que é fechado por um p elemento. Há um analisador de HTML na biblioteca padrão do Go, mas ele é muito tolerante: se o executarmos em nosso HTML quebrado, não obteremos um erro. Felizmente, a biblioteca padrão do Go também tem um analisador de XML que pode ser configurado para analisar HTML (graças a esta resposta do Stack Overflow)

Ir

  func Test_wellFormedHtml(t *testing.T) {
    templ := template.Should(template.ParseFiles("index.tmpl"))
    mannequin := todo.NewList()
    
    // render the template right into a buffer
    var buf bytes.Buffer
    err := templ.Execute(&buf, mannequin)
    if err != nil {
      panic(err)
    }
  
    // examine that the template may be parsed as (lenient) XML
    decoder := xml.NewDecoder(bytes.NewReader(buf.Bytes()))
    decoder.Strict = false
    decoder.AutoClose = xml.HTMLAutoClose
    decoder.Entity = xml.HTMLEntity
    for {
      _, err := decoder.Token()
      change err {
      case io.EOF:
        return // We're accomplished, it is legitimate!
      case nil:
        // do nothing
      default:
        t.Fatalf("Error parsing html: %s", err)
      }
    }
  }

fonte

Este código configura o analisador HTML para ter o nível certo de leniência para HTML e, em seguida, analisa o token HTML por token. De fato, vemos a mensagem de erro que queríamos:

--- FAIL: Test_wellFormedHtml (0.00s)
    index_template_test.go:61: Error parsing html: XML syntax error on line 4: sudden finish aspect 

Em Java, uma biblioteca versátil para usar é sopa j:

Java

  @Check
  void indexIsSoundHtml() {
      var template = Mustache.compiler().compile(
              new InputStreamReader(
                      getClass().getResourceAsStream("/index.tmpl")));
      var mannequin = new TodoList();
  
      var html = template.execute(mannequin);
  
      var parser = Parser.htmlParser().setTrackErrors(10);
      Jsoup.parse(html, "", parser);
      assertThat(parser.getErrors()).isEmpty();
  }

fonte

E vemos isso falhar:

java.lang.AssertionError: 
Anticipating empty however was:<(<1:13>: Surprising EndTag token () when in state (InBody),

Sucesso! Agora se copiarmos o conteúdo do modelo TodoMVC para nosso index.tmpl arquivo, o teste passa.

O teste, no entanto, é muito prolixo: extraímos duas funções auxiliares, para tornar a intenção do teste mais clara, e obtemos

Ir

  func Test_wellFormedHtml(t *testing.T) {
    mannequin := todo.NewList()
  
    buf := renderTemplate("index.tmpl", mannequin)
  
    assertWellFormedHtml(t, buf)
  }

fonte

Java

  @Check
  void indexIsSoundHtml() {
      var mannequin = new TodoList();
  
      var html = renderTemplate("/index.tmpl", mannequin);
  
      assertSoundHtml(html);
  }

fonte

Nível 2: testando a estrutura HTML

O que mais devemos testar?

Sabemos que a aparência de uma página só pode ser testada, em última análise, por um humano observando como ela é renderizada em um navegador. No entanto, geralmente há lógica em modelos, e queremos ser capazes de testar essa lógica.

Alguém pode ficar tentado a testar o HTML renderizado com igualdade de string, mas essa técnica falha na prática, porque os modelos contêm muitos detalhes que tornam as asserções de igualdade de string impraticáveis. As asserções se tornam muito prolixas e, ao ler a asserção, fica difícil entender o que estamos tentando provar.

O que precisamos é de uma técnica para afirmar que algumas peças do HTML renderizado corresponde ao que esperamos e a ignorar todos os detalhes com os quais não nos importamos. Uma maneira de fazer isso é executar consultas com o Seletor de idioma CSS: é uma linguagem poderosa que nos permite selecionar os elementos que nos interessam de todo o documento HTML. Uma vez selecionados esses elementos, nós (1) contamos que o número de elementos retornados é o que esperamos, e (2) que eles contêm o texto ou outro conteúdo que esperamos.

A interface do usuário que devemos gerar se parece com isto:

Testando modelos HTML

Há vários detalhes que são renderizados dinamicamente:

  1. O número de itens e seu conteúdo de texto mudam, obviamente
  2. O estilo do merchandise a fazer muda quando ele é concluído (por exemplo, o segundo)
  3. O texto “2 itens restantes” mudará com o número de itens não concluídos
  4. Um dos três botões “Todos”, “Ativo”, “Concluído” será destacado, dependendo da URL atual; por exemplo, se decidirmos que a URL que mostra apenas os itens “Ativos” é /livelyentão quando a URL atual for /livelyo botão “Ativo” deve estar rodeado por um retângulo vermelho fino
  5. O botão “Limpar concluído” só deve ficar visível se algum merchandise for concluído

Cada uma dessas preocupações pode ser testada com a ajuda de seletores CSS.

Este é um snippet do template TodoMVC (ligeiramente simplificado). Ainda não adicionei os bits dinâmicos, então o que vemos aqui é conteúdo estático, fornecido como exemplo:

índice.tmpl

  

fonte

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *