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)
}
}
}
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();
}
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) }
Java
@Check void indexIsSoundHtml() { var mannequin = new TodoList(); var html = renderTemplate("/index.tmpl", mannequin); assertSoundHtml(html); }
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:

Há vários detalhes que são renderizados dinamicamente:
- O número de itens e seu conteúdo de texto mudam, obviamente
- O estilo do merchandise a fazer muda quando ele é concluído (por exemplo, o segundo)
- O texto “2 itens restantes” mudará com o número de itens não concluídos
- 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” é
/lively
então quando a URL atual for/lively
o botão “Ativo” deve estar rodeado por um retângulo vermelho fino - 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