26 · Testando o seu roteamento offline¶
O roteador é determinístico: o mesmo turno, catálogo, vocabulário e contexto dão o mesmo plano, byte a byte. Ele roda no runtime licenciado na sua máquina, então os seus testes já leem cada turno sem rede. O que ainda chega ao serviço da Stalwart é o resto: compilar um rulebook, ler a sua conta, uma resolução. Um cassete grava essas respostas uma vez, e os seus testes as reproduzem sem rede. Este capítulo é a gravação, a reprodução e o que uma reprodução faz com uma chamada que ela não contém.
Idiomas
O seletor lê turnos escritos em inglês e em português do Brasil (pt-BR). O idioma é escolhido, nunca adivinhado a partir do turno: na página Settings do console (Language the selector reads), em language numa requisição de plano, ou no settings.language do seu arquivo de vocabulário; sem nenhum deles, inglês. Um turno em português pode ser digitado com os acentos ou sem eles. O português europeu não é uma gramática que o seletor tenha, e um turno em qualquer outra língua não é lido.
Grave uma vez, reproduza a cada execução¶
record(path) e replay(path), de stalwart_agents.client.testing, são transportes que um Client aceita.
A gravação conversa com o serviço da Stalwart e guarda cada resposta no arquivo; a reprodução responde a
partir do arquivo:
TURNS = ["mostre o pedido 4411", "cobre o pedido 4411 em 40,00", "zork o frobnicador"]
# Uma vez, de propósito: fala com o serviço da Stalwart e guarda cada resposta.
with Client(transport=record("service.cassette.json")) as client:
recorded = client.compile_rulebook(rulebook)
planned = [client.plan(turn, agent="desk", catalogue=catalogue).raw for turn in TURNS]
# Em cada execução de teste depois disso: as respostas do serviço tiradas do arquivo, sem rede.
with Client(transport=replay("service.cassette.json")) as client:
compiled = client.compile_rulebook(rulebook)
print(f"the rulebook compiled, the same record as recorded: {compiled == recorded}")
# Planos nunca entram no cassete: o runtime lê cada turno nesta máquina, nas duas execuções.
for turn, then in zip(TURNS, planned):
plan = client.plan(turn, agent="desk", catalogue=catalogue)
print(f"{turn!r}: {plan.outcome}, the same plan as before: {plan.raw == then}")
the rulebook compiled, the same record as recorded: True
'mostre o pedido 4411': run, the same plan as before: True
'cobre o pedido 4411 em 40,00': run, the same plan as before: True
'zork o frobnicador': unread, the same plan as before: True
CassetteMiss: the cassette holds no answer for GET /v1/account (key 89a246b29c7ddeeb)
Um cassete guarda as respostas do serviço, não a sua lógica. Ele é um arquivo de requisições e das suas respostas, indexado pelo método, caminho e corpo de cada requisição, então uma reprodução é exata para a mesma requisição e uma falta para qualquer outra. Versione-o ao lado dos seus testes.
Planos e verificações de ferramentas nunca entram num cassete. O runtime responde a eles no seu processo, sem requisição, então eles são tão exatos offline quanto online: na saída acima, o runtime leu cada turno nas duas execuções e deu o mesmo plano.
A manutenção do seu cliente com o serviço da Stalwart -- renovar a licença da sua máquina, o seu relatório de uso -- muda com o relógio, então um cassete não a grava nem a reproduz: numa máquina com licença, ela é enviada como seria sem cassete. Todo o resto a reprodução responde a partir do arquivo.
Quando uma reprodução não vale¶
with Client(transport=replay("service.cassette.json")) as client:
try:
client.account() # uma chamada que ninguém gravou
except CassetteMiss as miss:
print("CassetteMiss:", miss)
A última linha da saída:
CassetteMiss: o cassete não tem resposta para esta requisição. Uma chamada que ninguém gravou, ou uma cujo corpo mudou: o teste perguntou algo novo.
Gravando de novo, de propósito¶
Uma falta é o teste fazendo o seu trabalho: o seu código pergunta ao serviço algo que não perguntava antes. Grave de novo de propósito -- um job de CI que roda a gravação contra o serviço da Stalwart, ou um comando que um desenvolvedor roda -- e revise o diff do cassete como código: cada resposta alterada é uma chamada que agora responde de outro jeito.
Os limites¶
Os limites do capítulo 20 valem aqui, e mais estes:
- Um cassete prova que o seu código faz o que fazia quando ele foi gravado, com as respostas do serviço daquele dia. Ele não diz nada de chamadas que não contém.
- O runtime continua respondendo sob a sua licença: uma máquina de testes lê turnos e verifica chamadas com uma licença ativada, com ou sem cassete.