No meu aplicativo para testes de unidade, nossa equipe depende muito da ferramenta para executar testes assíncronos - ConcurrencyExtras - withMainSerialExecutor
Essa ferramenta adiciona a possibilidade de lançar código de teste no thread principal, e também podemos adicionar await Task.yield()
um bloco para aguardar a conclusão de alguma operação demorada.
Funciona bem, mas parece que o tempo de execução dos testes aumentou, o que eu quero é encurtar o tempo de execução.
Até agora, a única solução que encontrei é sugerida aqui : adicionar
override func invokeTest() {
withMainSerialExecutor {
super.invokeTest()
}
}
No início de cada teste, e remove withMainSerialExecutor
em todos os lugares. Funciona, e agora meus testes assíncronos rodam mais rápido.
Minha pergunta é: por que ele reduziu o tempo de execução dos testes assíncronos? O que eu fiz foi basicamente forçar todos os testes a serem executados no thread principal, mas withMainSerialExecutor
o bloco adicionado manualmente fez a mesma coisa. Talvez alguém tenha outra solução para reduzir o tempo de execução dos testes assíncronos?
Exemplo de teste que passou da zona "longa" (mais de 0,1 segundos) para a curta
func testHasLoaded() async {
/// Arrange
sut = makeVM()
var hasLoaded = String()
sut.dataHasFinishedLoading = {
hasLoaded = "hasLoaded"
}
/// Assert
await Task.megaYield()
XCTAssertEqual(hasLoaded, "hasLoaded")
}
Original (0,11 segundos):
func testHasLoaded() async {
withMainSerialExecutor {
/// Arrange
sut = makeVM()
var hasLoaded = String()
sut.dataHasFinishedLoading = {
hasLoaded = "hasLoaded"
}
/// Assert
await Task.megaYield()
XCTAssertEqual(hasLoaded, "hasLoaded")
}
}
Eu poderia sugerir que o rendimento (ou, pior, o “mega rendimento”) é um antipadrão.
Os testes suportam simultaneidade Swift, então eu chamaria
await
para seudataHasFinishedLoading
fechamento. Podemos fazer isso envolvendo isso em uma continuação. Há muitas maneiras de fazer isso, mas talvez umAsyncStream
:Para sua informação, em um comentário você disse:
Expectativas não aumentam significativamente o tempo de execução do teste. Esta é uma afirmação incorreta frequentemente repetida no Stack Overflow. O timeout é o pior cenário se houver um erro e a expectativa não for satisfeita dentro de um prazo razoável. Mas se a expectativa for cumprida em tempo hábil, a expectativa termina imediatamente e não espera pelo timeout.
Agora, se você não gosta desse tempo limite de segurança medido em segundos, então, tudo bem, defina um tempo limite menor para esse cenário de "trabalho assíncrono não termina em tempo hábil". Talvez defina tempos limite medidos em dezenas ou centenas de milissegundos em vez de segundos. Mas seu "mega rendimento" faz exatamente a mesma coisa: adiciona um atraso. Claro, essa espera provavelmente não é medida em segundos (não podemos dizer sem ver sua
megaYield
implementação), mas ainda espera. Esse fluxo de trabalho de rendimento é, sem dúvida, pior, porque vai inserir um atraso independentemente de quando o trabalho assíncrono realmente for concluído.Você sempre quer apenas um tempo limite para cobrir seu pior cenário, mas em cenários bem-sucedidos, para terminar imediatamente. Você nunca quer que um teste espere mais do que o necessário. É isso que as expectativas alcançam. É isso que o padrão acima
async
tambémawait
alcança. Mas adicionar um monte de yields ou quaisquer atrasos fixos é um passo na direção errada.Minha crítica às expectativas se limita ao fato de que elas são um artefato dos padrões legados do XCTest, mas não estão disponíveis no novo framework Swift Testing
async
. Este novo framework de testes usa padrões -await
(é por isso que usei o padrão acima). Por esse motivo, pode-se querer evitar introduzir expectativas neste ponto, para minimizar a quantidade de retrabalho necessária se/quando você eventualmente migrar para o framework Swift Testing mais novo.