Testes unitários: asserções adiadas com Linq

18

Posso adicionar asserções adiadas como esta

var actualKittens = actualKittens.Select(kitten => {
    Assert.IsСute(kitten);
    return kitten
});

Por quê? Então, eu posso iterar apenas uma vez, mesmo com instruções que esperam uma coleta materializada, por exemplo:

CollectionAssert.AreEquivalent(expectedKittens, actualKittens.ToList());

E também pode ser não apenas Select, mas um método com o iterador definido e com muitas verificações e lógica (por exemplo, algumas contagens e filtros).

A semente da dúvida é a complexidade de ler e depurar esse código em caso de falha no teste.

SerG
fonte
1
Eu não confiaria nisso para testes, mas às vezes não há problema em fazer isso no código de produção se não houver uma solução melhor. Você pode se tornar uma função auxiliar sequence.WithSideEffect(item => Assert.IsCute(item))para torná-la mais limpa.
usr
@usr Parece que você percebeu a falha principal dessa maneira - efeito colateral de um iterador.
SerG 03/07
Você ainda não precisa repetir duas vezes, uma vez para gerar a lista e novamente para compará-la expectedKittens? Você acabou de ocultar as iterações por trás das chamadas de método.
precisa
@ IlusiveBrian Nesse sentido, no exemplo, sim. Ainda é menor do que com adicional .All().
SerG 03/07

Respostas:

37

Tudo bem adicionar declarações adiadas como esta [..]

Não é não. Por quê? Porque, se por algum motivo você remover a segunda declaração, o teste ainda ficará verde e você achará que ainda funciona, mas não funciona, pois a coleção não será enumerada. Se você tiver duas ou mais afirmações independentes, elas continuarão realizando seu trabalho, mesmo que você desative uma delas.

Considere esta combinação:

Assert.IsTrue(actualKittens.All(x => x.IsCute());
CollectionAssert.AreEquivalent(expectedKittens, actualKittens.ToList());

Agora, mesmo que você desative ou remova uma das afirmações, a outra ainda fará seu trabalho. Além disso, se você esquecer de materializar a coleção, poderá levar mais tempo para executar, mas ainda funcionará. Testes independentes são mais robustos e confiáveis.

Há também um segundo não . Não tenho certeza de como outras estruturas lidam com isso, mas se você estiver usando a plataforma MS Test, não saberá qual teste falhou. Se você clicar duas vezes no teste que falhou, ele mostrará o CollectionAssertque falhou, mas, na realidade, foi o aninhado Assertque deu errado e será extremamente difícil depurar. Aqui está um exemplo:

    [TestMethod]
    public void TestMethod()
    {
        var numbers = new[] { 1, 2, 3 }.Select(x =>
        {
            Assert.Fail("Wrong number.");
            return x;
        });

        // This will fail and you won't be sure why.
        CollectionAssert.AreEqual(new[] { 1, 2, 3 }, numbers.ToList()); 

    }

Isso significa que o primeiro teste é realmente inútil porque não ajuda a encontrar um bug. Você não sabe se houve falha porque um número era inválido ou porque as duas coleções eram diferentes.


Por quê? Para que eu possa iterar apenas uma vez, mesmo com instruções que esperam uma coleta materializada

Eu me pergunto por que você se importa com isso? Estes são testes de unidade. Você não precisa otimizar cada um deles e, geralmente, os testes não requerem milhões de itens, portanto o desempenho não deve ser uma preocupação.

Você precisará manter esses testes. Por que você deve torná-los mais complexos do que o necessário? Escreva afirmações simples que funcionem.

t3chb0t
fonte
Se, por algum motivo, uma asserção precisar ser ocultada no fluxo de controle, uma maneira de garantir que foi executada é manter um contador / sinalizador que seja incrementado / configurado como true antes da asserção aninhada. Posteriormente, podemos afirmar que o fluxo de controle esperado foi obtido verificando esse contador. Não é perfeito, mas aborda amplamente suas primeiras críticas.
amon
1
Além disso, você ou outra pessoa voltaria à declaração adiada em seis meses e perderia tempo tentando descobrir isso.
21417 DavidTheWin
Há algo errado com o seu exemplo. Chamar ToListiterará o enumerável, não é?
precisa
1
@RubberDuck sim, ele falhará e também falhará, mas não no Assert.Failmas no CollectionAsserte você não poderá dizer qual afirmação realmente deu errado. Quero dizer, o VS não vai se concentrar, Assert.Failmas no outro ... agora você pode depurar.
t3chb0t