Como você sabe o que testar ao escrever testes de unidade? [fechadas]


127

Usando C #, preciso de uma classe chamada Userque possua nome de usuário, senha, sinalizador ativo, nome, sobrenome, nome completo, etc.

Deve haver métodos para autenticar e salvar um usuário. Acabei de escrever um teste para os métodos? E eu preciso me preocupar em testar as propriedades, pois elas são getter e setters da .Net?


Este post vai ajudar com o estreitamento da questão mais ampla: earnestengineer.blogspot.com/2018/03/... Você pode tomar estas diretrizes para concentrar a sua pergunta
Lindsay Morsillo

Lembre-se de que as senhas não devem ser armazenadas como texto sem formatação.
Anderson Anderson

Respostas:


131

Muitas ótimas respostas para isso também estão na minha pergunta: " Iniciando TDD - Desafios? Soluções? Recomendações? "

Posso recomendar também que dê uma olhada no meu post no blog (que foi parcialmente inspirado pela minha pergunta), tenho um bom feedback sobre isso. Nomeadamente:

Não sei por onde começar?

  • Começar de novo. Pense apenas em escrever testes quando estiver escrevendo um novo código. Isso pode ser uma reformulação do código antigo ou de um recurso completamente novo.
  • Comece simples. Não saia correndo e tentando entender a estrutura de teste, além de ser do tipo TDD. Debug.Assert funciona bem. Use-o como ponto de partida. Não mexe com o seu projeto ou cria dependências.
  • Comece positivo. Você está tentando melhorar sua arte, sinta-se bem com isso. Eu já vi muitos desenvolvedores por aí que estão felizes em estagnar e não tentar coisas novas para melhorar a si mesmos. Você está fazendo a coisa certa, lembre-se disso e isso ajudará a impedi-lo de desistir.
  • Comece pronto para um desafio. É muito difícil começar a testar. Espere um desafio, mas lembre-se - os desafios podem ser superados.

Teste apenas o que você espera

Eu tive problemas reais quando comecei, porque estava constantemente sentado, tentando descobrir todos os problemas possíveis que poderiam ocorrer e, em seguida, tentando testá-los e corrigi-los. Esta é uma maneira rápida de sentir dor de cabeça. O teste deve ser um processo YAGNI real. Se você sabe que há um problema, escreva um teste para ele. Caso contrário, não se preocupe.

Teste apenas uma coisa

Cada caso de teste deve apenas testar uma coisa. Se você se colocar "e" no nome do caso de teste, estará fazendo algo errado.

Espero que isso signifique que podemos seguir em frente "getters and setters" :)


2
"Se você sabe que há um problema, escreva um teste para ele. Caso contrário, não se incomode." Eu discordo da maneira como isso é formulado. Fiquei com a impressão de que os testes de unidade deveriam cobrir todos os caminhos de execução possíveis.
Corin Blaikie 15/09/08

3
Enquanto alguns podem advogar tais coisas, eu pessoalmente não. Um bom 90% da minha dor de cabeça veio simplesmente tentando fazer "tudo". Eu digo teste para o que você espera que aconteça (para que você saiba que está recebendo os valores certos de volta), mas não tente descobrir tudo. YAGNI.
Rob Cooper

4
Eu também defendo a abordagem "teste seus bugs". Se todos tivéssemos tempo e paciência infinitos, testaríamos todos os caminhos de execução possíveis. Mas não temos, então você tem que gastar seu esforço onde isso terá o maior efeito.
Schwern 18/10/08

63

Teste seu código, não o idioma.

Um teste de unidade como:

Integer i = new Integer(7);
assert (i.instanceOf(integer));

só é útil se você estiver escrevendo um compilador e houver uma chance diferente de zero de que seu instanceofmétodo não esteja funcionando.

Não teste coisas que você pode confiar no idioma para impor. No seu caso, eu focaria nos métodos de autenticação e salvamento - e escreveria testes para garantir que eles pudessem lidar com valores nulos em qualquer um ou todos esses campos normalmente.


1
Ponto bom em "Não teste o framework" - Algo que também aprendi quando era novo nisso. + 1'ed :)
Rob Cooper

38

Isso me levou a testes de unidade e me deixou muito feliz

Nós apenas começamos a fazer testes de unidade. Durante muito tempo, sabia que seria bom começar a fazê-lo, mas não fazia ideia de como começar e, mais importante, o que testar.

Em seguida, tivemos que reescrever uma parte importante do código em nosso programa de contabilidade. Essa parte era muito complexa, pois envolvia muitos cenários diferentes. A parte da qual estou falando é um método para pagar faturas de venda e / ou compra já inseridas no sistema contábil.

Só não sabia como começar a codificá-lo, pois havia muitas opções de pagamento diferentes. Uma fatura pode ser de US $ 100, mas o cliente transferiu apenas US $ 99. Talvez você tenha enviado faturas de vendas a um cliente, mas também tenha adquirido desse cliente. Então você o vendeu por US $ 300, mas você comprou por US $ 100. Você pode esperar que seu cliente pague US $ 200 para liquidar o saldo. E se você vendesse por US $ 500, mas o cliente pagasse apenas US $ 250?

Então, eu tinha um problema muito complexo para resolver com muitas possibilidades de que um cenário funcionasse perfeitamente, mas estaria errado em outro tipo de combinação de invocação / pagamento.

Foi aqui que os testes de unidade foram realizados em socorro.

Comecei a escrever (dentro do código de teste) um método para criar uma lista de faturas, tanto para vendas quanto para compras. Então, escrevi um segundo método para criar o pagamento real. Normalmente, um usuário insere essas informações por meio de uma interface do usuário.

Depois, criei o primeiro TestMethod, testando um pagamento muito simples de uma única fatura sem descontos no pagamento. Toda a ação no sistema aconteceria quando um pagamento bancário fosse salvo no banco de dados. Como você pode ver, criei uma fatura, criei um pagamento (uma transação bancária) e salvei a transação no disco. Nas minhas afirmações, coloco quais devem ser os números corretos que terminam na transação bancária e na fatura vinculada. Verifico o número de pagamentos, os valores do pagamento, o valor do desconto e o saldo da fatura após a transação.

Após a execução do teste, eu iria ao banco de dados e verificaria se o que eu esperava estava lá.

Depois que escrevi o teste, comecei a codificar o método de pagamento (parte da classe BankHeader). Na codificação, eu apenas me preocupei com o código para fazer o primeiro teste passar. Ainda não pensei nos outros cenários mais complexos.

Eu executei o primeiro teste, corrigi um pequeno bug até que meu teste passasse.

Então comecei a escrever o segundo teste, desta vez trabalhando com um desconto de pagamento. Depois de escrever o teste, modifiquei o método de pagamento para oferecer suporte a descontos.

Ao testar a correção com um desconto de pagamento, também testei o pagamento simples. Ambos os testes devem passar, é claro.

Depois, trabalhei até os cenários mais complexos.

1) Pense em um novo cenário

2) Escreva um teste para esse cenário

3) Execute esse teste único para ver se passaria

4) Caso contrário, eu depuraria e modificaria o código até que ele passasse.

5) Ao modificar o código, continuei executando todos os testes

Foi assim que consegui criar meu método de pagamento muito complexo. Sem o teste de unidade, eu não sabia como começar a codificar, o problema parecia esmagador. Com o teste, eu poderia começar com um método simples e estendê-lo passo a passo com a garantia de que os cenários mais simples ainda funcionariam.

Tenho certeza de que o uso do teste de unidade me salvou alguns dias (ou semanas) de codificação e garante mais ou menos a correção do meu método.

Se depois pensar em um novo cenário, posso adicioná-lo aos testes para ver se está funcionando ou não. Caso contrário, posso modificar o código, mas ainda assim tenha certeza de que os outros cenários ainda estão funcionando corretamente. Isso economizará dias e dias na fase de manutenção e correção de erros.

Sim, mesmo o código testado ainda pode ter erros se um usuário faz coisas que você não pensou ou o impediu de fazer

Abaixo estão apenas alguns dos testes que criei para testar minha forma de pagamento.

public class TestPayments
{
    InvoiceDiaryHeader invoiceHeader = null;
    InvoiceDiaryDetail invoiceDetail = null;
    BankCashDiaryHeader bankHeader = null;
    BankCashDiaryDetail bankDetail = null;



    public InvoiceDiaryHeader CreateSales(string amountIncVat, bool sales, int invoiceNumber, string date)
    {
        ......
        ......
    }

    public BankCashDiaryHeader CreateMultiplePayments(IList<InvoiceDiaryHeader> invoices, int headerNumber, decimal amount, decimal discount)
    {
       ......
       ......
       ......
    }


    [TestMethod]
    public void TestSingleSalesPaymentNoDiscount()
    {
        IList<InvoiceDiaryHeader> list = new List<InvoiceDiaryHeader>();
        list.Add(CreateSales("119", true, 1, "01-09-2008"));
        bankHeader = CreateMultiplePayments(list, 1, 119.00M, 0);
        bankHeader.Save();

        Assert.AreEqual(1, bankHeader.BankCashDetails.Count);
        Assert.AreEqual(1, bankHeader.BankCashDetails[0].Payments.Count);
        Assert.AreEqual(119M, bankHeader.BankCashDetails[0].Payments[0].PaymentAmount);
        Assert.AreEqual(0M, bankHeader.BankCashDetails[0].Payments[0].PaymentDiscount);
        Assert.AreEqual(0, bankHeader.BankCashDetails[0].Payments[0].InvoiceHeader.Balance);
    }

    [TestMethod]
    public void TestSingleSalesPaymentDiscount()
    {
        IList<InvoiceDiaryHeader> list = new List<InvoiceDiaryHeader>();
        list.Add(CreateSales("119", true, 2, "01-09-2008"));
        bankHeader = CreateMultiplePayments(list, 2, 118.00M, 1M);
        bankHeader.Save();

        Assert.AreEqual(1, bankHeader.BankCashDetails.Count);
        Assert.AreEqual(1, bankHeader.BankCashDetails[0].Payments.Count);
        Assert.AreEqual(118M, bankHeader.BankCashDetails[0].Payments[0].PaymentAmount);
        Assert.AreEqual(1M, bankHeader.BankCashDetails[0].Payments[0].PaymentDiscount);
        Assert.AreEqual(0, bankHeader.BankCashDetails[0].Payments[0].InvoiceHeader.Balance);
    }

    [TestMethod]
    [ExpectedException(typeof(ApplicationException))]
    public void TestDuplicateInvoiceNumber()
    {
        IList<InvoiceDiaryHeader> list = new List<InvoiceDiaryHeader>();
        list.Add(CreateSales("100", true, 2, "01-09-2008"));
        list.Add(CreateSales("200", true, 2, "01-09-2008"));

        bankHeader = CreateMultiplePayments(list, 3, 300, 0);
        bankHeader.Save();
        Assert.Fail("expected an ApplicationException");
    }

    [TestMethod]
    public void TestMultipleSalesPaymentWithPaymentDiscount()
    {
        IList<InvoiceDiaryHeader> list = new List<InvoiceDiaryHeader>();
        list.Add(CreateSales("119", true, 11, "01-09-2008"));
        list.Add(CreateSales("400", true, 12, "02-09-2008"));
        list.Add(CreateSales("600", true, 13, "03-09-2008"));
        list.Add(CreateSales("25,40", true, 14, "04-09-2008"));

        bankHeader = CreateMultiplePayments(list, 5, 1144.00M, 0.40M);
        bankHeader.Save();

        Assert.AreEqual(1, bankHeader.BankCashDetails.Count);
        Assert.AreEqual(4, bankHeader.BankCashDetails[0].Payments.Count);
        Assert.AreEqual(118.60M, bankHeader.BankCashDetails[0].Payments[0].PaymentAmount);
        Assert.AreEqual(400, bankHeader.BankCashDetails[0].Payments[1].PaymentAmount);
        Assert.AreEqual(600, bankHeader.BankCashDetails[0].Payments[2].PaymentAmount);
        Assert.AreEqual(25.40M, bankHeader.BankCashDetails[0].Payments[3].PaymentAmount);

        Assert.AreEqual(0.40M, bankHeader.BankCashDetails[0].Payments[0].PaymentDiscount);
        Assert.AreEqual(0, bankHeader.BankCashDetails[0].Payments[1].PaymentDiscount);
        Assert.AreEqual(0, bankHeader.BankCashDetails[0].Payments[2].PaymentDiscount);
        Assert.AreEqual(0, bankHeader.BankCashDetails[0].Payments[3].PaymentDiscount);

        Assert.AreEqual(0, bankHeader.BankCashDetails[0].Payments[0].InvoiceHeader.Balance);
        Assert.AreEqual(0, bankHeader.BankCashDetails[0].Payments[1].InvoiceHeader.Balance);
        Assert.AreEqual(0, bankHeader.BankCashDetails[0].Payments[2].InvoiceHeader.Balance);
        Assert.AreEqual(0, bankHeader.BankCashDetails[0].Payments[3].InvoiceHeader.Balance);
    }

    [TestMethod]
    public void TestSettlement()
    {
        IList<InvoiceDiaryHeader> list = new List<InvoiceDiaryHeader>();
        list.Add(CreateSales("300", true, 43, "01-09-2008")); //Sales
        list.Add(CreateSales("100", false, 6453, "02-09-2008")); //Purchase

        bankHeader = CreateMultiplePayments(list, 22, 200, 0);
        bankHeader.Save();

        Assert.AreEqual(1, bankHeader.BankCashDetails.Count);
        Assert.AreEqual(2, bankHeader.BankCashDetails[0].Payments.Count);
        Assert.AreEqual(300, bankHeader.BankCashDetails[0].Payments[0].PaymentAmount);
        Assert.AreEqual(-100, bankHeader.BankCashDetails[0].Payments[1].PaymentAmount);
        Assert.AreEqual(0, bankHeader.BankCashDetails[0].Payments[0].InvoiceHeader.Balance);
        Assert.AreEqual(0, bankHeader.BankCashDetails[0].Payments[1].InvoiceHeader.Balance);
    }

1
Encontrou um erro no seu teste de unidade! Você repete esta linha em vez de incluir um 2 em um deles:Assert.AreEqual(0, bankHeader.BankCashDetails[0].Payments[3].InvoiceHeader.Balance);
Ryan Peschel 21/10

2
Você diz: "Após a execução do teste, eu procurava no banco de dados e verificava se o que eu esperava estava lá". Este é um bom exemplo de um teste de integração entre componentes do seu sistema - não um teste de unidade isolado de um único trecho de código.
JTech

2
Você também violou a regra de mais de uma afirmação por teste.
Steve

13

Se eles realmente são triviais, não se preocupe em testar. Por exemplo, se eles são implementados assim;

public class User
{
    public string Username { get; set; }
    public string Password { get; set; }
}

Se, por outro lado, você estiver fazendo algo inteligente (como criptografar e descriptografar a senha no getter / setter), faça um teste.


10

A regra é que você precisa testar cada parte da lógica que escreve. Se você implementou alguma funcionalidade específica nos getters e setters, acho que vale a pena testar. Se eles apenas atribuírem valores a alguns campos particulares, não se preocupe.


6

Esta questão parece ser uma questão de onde se pode traçar quais métodos são testados e quais não.

Os levantadores e os levantadores para atribuição de valor foram criados com consistência e crescimento futuro em mente, e prevendo que em algum momento no caminho o levantador / levantador possa evoluir para operações mais complexas. Faria sentido implementar testes unitários desses métodos, também por uma questão de consistência e crescimento futuro.

A confiabilidade do código, especialmente durante as alterações para adicionar funcionalidade adicional, é o objetivo principal. Eu não estou ciente de que alguém já foi demitido por incluir setters / getters na metodologia de teste, mas estou certo de que existem pessoas que desejavam ter testado métodos que eles conheciam ou que podem lembrar que eram simples invólucros set / get, mas isso não era mais o caso.

Talvez outro membro da equipe tenha expandido os métodos set / get para incluir a lógica que agora precisa ser testada, mas não criou os testes. Mas agora seu código está chamando esses métodos e você não sabe que eles mudaram e precisam de testes detalhados, e os testes que você faz no desenvolvimento e no controle de qualidade não acionam o defeito, mas dados comerciais reais no primeiro dia do lançamento acioná-lo.

Os dois companheiros de equipe agora debaterão sobre quem deixou a bola cair e não conseguiu fazer testes de unidade quando o set / se transformou para incluir lógica que pode falhar, mas não é coberta por um teste de unidade. O companheiro de equipe que originalmente escreveu o set / gets terá mais facilidade em sair dessa limpeza se os testes forem implementados desde o primeiro dia no set / gets simples.

Minha opinião é que alguns minutos de tempo "desperdiçado" cobrindo TODOS os métodos com testes de unidade, mesmo que triviais, podem economizar dias de dor de cabeça no caminho e perda de dinheiro / reputação dos negócios e perda do emprego de alguém.

E o fato de você envolver métodos triviais com testes de unidade pode ser visto por esse colega de equipe júnior quando eles mudam os métodos triviais para outros não triviais e os solicitam a atualizar o teste, e agora ninguém está com problemas porque o defeito foi contido de alcançar a produção.

A maneira como codificamos e a disciplina que pode ser vista em nosso código podem ajudar os outros.


4

Outra resposta canônica. Creio que isto de Ron Jeffries:

Teste apenas o código que você deseja trabalhar.


3

Testar o código padrão é uma perda de tempo, mas como Slavo diz, se você adicionar um efeito colateral aos seus getters / setters, deverá escrever um teste para acompanhar essa funcionalidade.

Se você está desenvolvendo um desenvolvimento orientado a testes, escreva primeiro o contrato (por exemplo, interface) e, em seguida, escreva os testes para exercitar a interface que documenta os resultados / comportamentos esperados. Em seguida, escreva seus métodos, sem tocar no código dos testes de unidade. Por fim, pegue uma ferramenta de cobertura de código e verifique se seus testes exercitam todos os caminhos lógicos em seu código.


3

Código realmente trivial, como getters e setters que não têm comportamento extra, além de definir um campo privado, é um exagero a ser testado. No 3.0, o C # ainda possui um pouco de açúcar sintático, onde o compilador cuida do campo privado, para que você não precise programar isso.

Geralmente escrevo muitos testes muito simples para verificar o comportamento que espero das minhas aulas. Mesmo que seja simples, como adicionar dois números. Eu mudo muito entre escrever um teste simples e escrever algumas linhas de código. A razão para isso é que eu posso mudar o código sem ter medo de quebrar as coisas em que não pensei.


Que bom que você fez uma boa observação do princípio do KISS. Muitas vezes, tenho testes que são literalmente como 2-3 linhas de código, testes realmente pequenos e simples. Fácil de grok e difícil de quebrar :) + 1'ed
Rob Cooper

3

Você deveria testar tudo. No momento, você tem getters e setters, mas um dia você pode alterá-los um pouco, talvez para validar ou algo mais. Os testes que você escrever hoje serão usados ​​amanhã para garantir que tudo continue funcionando normalmente. Ao escrever o teste, você deve esquecer considerações como "agora é trivial". Em um contexto ágil ou orientado a teste, você deve testar assumindo refatoração futura. Além disso, você tentou inserir valores realmente estranhos, como sequências extremamente longas ou outro conteúdo "ruim"? Bem, você deve ... nunca assumir o quanto seu código pode ser abusado no futuro.

Geralmente, acho que escrever exaustivos testes para o usuário é cansativo. Por outro lado, embora ele sempre ofereça informações valiosas sobre como o aplicativo deve funcionar e ajude a descartar suposições fáceis (e falsas) (como: o nome do usuário sempre terá menos de 1.000 caracteres).


3

Para módulos simples que podem acabar em um kit de ferramentas ou em um tipo de projeto de código aberto, você deve testar o máximo possível, incluindo os getters e setters triviais. O que você deseja ter em mente é que gerar um teste de unidade ao escrever um módulo específico é bastante simples e direto. Adicionar getters e setters é um código mínimo e pode ser tratado sem muita reflexão. No entanto, depois que seu código é colocado em um sistema maior, esse esforço extra pode protegê-lo contra alterações no sistema subjacente, como alterações de tipo em uma classe base. Testar tudo é a melhor maneira de ter uma regressão completa.


2

Não faz mal escrever testes de unidade para seus getters e setters. No momento, eles podem apenas executar / obter conjuntos de campo, mas no futuro você pode ter uma lógica de validação ou dependências entre propriedades que precisam ser testadas. É mais fácil escrevê-lo agora enquanto você pensa sobre isso, lembrando-se de adaptá-lo se chegar a hora.


bem, se seus getters / setters precisam de testes de unidade, deve haver alguma lógica associada a eles, o que significa que a lógica deve ser escrita dentro deles; se eles não tiverem lógica, nenhum teste de unidade precisará ser escrito.
Pop Catalin

2
Seu argumento é que a lógica possa ser adicionada a eles mais tarde.
LegendLength

2

em geral, quando um método é definido apenas para determinados valores, teste valores dentro e além da borda do que é aceitável. Em outras palavras, verifique se o seu método faz o que deveria fazer, mas nada mais . Isso é importante, porque quando você falhar, você quer falhar mais cedo.

Nas hierarquias de herança, certifique-se de testar a conformidade com LSP .

Testar getters e setters padrão não me parece muito útil, a menos que você esteja planejando validar mais tarde.


2

bem, se você acha que pode quebrar, escreva um teste. Normalmente eu não testei o setter / getter, mas vamos dizer que você cria um para User.Name, que concatena o nome e o sobrenome, eu escreveria um teste para que, se alguém alterar a ordem do sobrenome e do nome, pelo menos ele saberia ele mudou algo que foi testado.


2

A resposta canônica é "teste qualquer coisa que possa quebrar". Se você tiver certeza de que as propriedades não quebrarão, não as teste.

E uma vez que algo foi quebrado (você encontra um bug), obviamente significa que você precisa testá-lo. Escreva um teste para reproduzir o erro, observe-o falhar, corrija-o e depois passe no teste.


1

Como eu entendo os testes de unidade no contexto do desenvolvimento ágil, Mike, sim, você precisa testar os getters e setters (supondo que eles sejam visíveis publicamente). Todo o conceito de teste de unidade é testar a unidade de software, que é uma classe nesse caso, como uma caixa preta . Como os getters e setters são visíveis externamente, é necessário testá-los junto com Authenticate and Save.


1

Se os métodos Autenticar e Salvar usarem as propriedades, seus testes tocarão indiretamente nas propriedades. Desde que as propriedades estejam apenas fornecendo acesso aos dados, o teste explícito não deve ser necessário (a menos que você esteja buscando 100% de cobertura).


1

Eu testaria seus getters e setters. Dependendo de quem está escrevendo o código, algumas pessoas alteram o significado dos métodos getter / setter. Eu vi a inicialização variável e outra validação como parte dos métodos getter. Para testar esse tipo de coisa, você deseja testes de unidade que cubram esse código explicitamente.


1

Pessoalmente, eu "testaria qualquer coisa que possa quebrar" e o getter simples (ou propriedades automáticas ainda melhores) não quebrará. Eu nunca tive uma declaração de retorno simples falhar e, portanto, nunca tenho teste para eles. Se os getters tiverem cálculos dentro deles ou alguma outra forma de declaração, eu certamente adicionaria testes para eles.

Pessoalmente, uso o Moq como uma estrutura de objeto simulada e, em seguida, verifico se meu objeto chama os objetos circundantes da maneira que deveria.


1

Você deve cobrir a execução de todos os métodos da classe com UT e verificar o valor de retorno do método. Isso inclui getters e setters, especialmente no caso de os membros (propriedades) serem classes complexas, o que requer grande alocação de memória durante sua inicialização. Ligue para o setter com uma string muito grande, por exemplo (ou algo com símbolos gregos) e verifique se o resultado está correto (não truncado, a codificação é boa etc.)

No caso de números inteiros simples, isso também se aplica - o que acontece se você passar muito tempo em vez de inteiro? Essa é a razão pela qual você escreve UT :)


1

Eu não testaria a configuração real das propriedades. Eu ficaria mais preocupado com o modo como essas propriedades são preenchidas pelo consumidor e com o que elas as preenchem. Em qualquer teste, você deve avaliar os riscos com o tempo / custo do teste.


1

Você deve testar "todo bloco de código não trivial" usando testes de unidade na medida do possível.

Se suas propriedades são triviais e é improvável que alguém apresente um bug, deve ser seguro não testá-las.

Seus métodos Authenticate () e Save () parecem bons candidatos para teste.


1

Idealmente, você teria feito seus testes de unidade enquanto escrevia a classe. É assim que você deve fazer isso ao usar o Test Driven Development. Você adiciona os testes à medida que implementa cada ponto de função, certificando-se de cobrir também os casos extremos.

Escrever os testes depois é muito mais doloroso, mas factível.

Aqui está o que eu faria na sua posição:

  1. Escreva um conjunto básico de testes que testem a função principal.
  2. Obtenha o NCover e execute-o em seus testes. Sua cobertura de teste provavelmente será de cerca de 50% neste momento.
  3. Continue adicionando testes que cobrem seus casos extremos até obter uma cobertura de cerca de 80% a 90%

Isso deve fornecer um bom conjunto de testes de unidade que funcionarão como um bom buffer contra regressões.

O único problema com essa abordagem é que o código precisa ser projetado para ser testável dessa maneira. Se você cometeu algum erro de acoplamento desde o início, não poderá obter alta cobertura com muita facilidade.

É por isso que é realmente importante escrever os testes antes de escrever o código. Obriga você a escrever um código que seja fracamente acoplado.


1

Não teste obviamente o código de funcionamento (padrão). Portanto, se seus setters e getters são apenas "propertyvalue = value" e "return propertyvalue", não faz sentido testá-lo.


1

Mesmo get / set pode ter conseqüências estranhas, dependendo de como elas foram implementadas, portanto devem ser tratadas como métodos.

Cada teste destes precisará especificar conjuntos de parâmetros para as propriedades, definindo propriedades aceitáveis ​​e inaceitáveis ​​para garantir que as chamadas retornem / falhem da maneira esperada.

Você também precisa estar ciente das dicas de segurança, como um exemplo de injeção SQL, e testá-las.

Então, sim, você precisa se preocupar em testar as propriedades.


1

Eu acredito que é bobagem testar getters e setters quando eles fazem apenas uma operação simples. Pessoalmente, não escrevo testes de unidade complexos para cobrir qualquer padrão de uso. Eu tento escrever testes suficientes para garantir que eu lidei com o comportamento normal de execução e com o máximo de casos de erro que consigo imaginar. Escreverei mais testes de unidade como resposta a relatórios de erros. Uso o teste de unidade para garantir que o código atenda aos requisitos e para facilitar futuras modificações. Sinto-me muito mais disposto a alterar o código quando sei que, se eu quebrar algo, um teste falhará.


1

Eu escreveria um teste para qualquer coisa para a qual você esteja escrevendo um código que possa ser testado fora da interface GUI.

Normalmente, qualquer lógica que escrevo que tenha qualquer lógica de negócios que eu coloque dentro de outra camada ou camada de lógica de negócios.

Em seguida, escrever testes para qualquer coisa que faça algo é fácil de fazer.

Primeiro passe, escreva um teste de unidade para cada método público em sua "Camada lógica de negócios".

Se eu tivesse uma aula como esta:

   public class AccountService
    {
        public void DebitAccount(int accountNumber, double amount)
        {

        }

        public void CreditAccount(int accountNumber, double amount)
        {

        }

        public void CloseAccount(int accountNumber)
        {

        }
    }

A primeira coisa que eu faria antes de escrever qualquer código sabendo que eu tinha essas ações para executar seria começar a escrever testes de unidade.

   [TestFixture]
    public class AccountServiceTests
    {
        [Test]
        public void DebitAccountTest()
        {

        }

        [Test]
        public void CreditAccountTest()
        {

        }

        [Test]
        public void CloseAccountTest()
        {

        }
    }

Escreva seus testes para validar o código que você escreveu para fazer alguma coisa. Se você iterar sobre uma coleção de coisas e alterar algo sobre cada uma delas, escreva um teste que faça a mesma coisa e afirme o que realmente aconteceu.

Há muitas outras abordagens que você pode adotar, como o Behavoir Driven Development (BDD), que é mais envolvido e não é um ótimo lugar para começar com suas habilidades de teste de unidade.

Portanto, a moral da história é: teste qualquer coisa que faça qualquer coisa com a qual você possa estar preocupado; mantenha os testes de unidade testando coisas específicas de tamanho pequeno; muitos testes são bons.

Mantenha sua lógica de negócios fora da camada da interface do usuário para que você possa escrever facilmente testes para eles e será bom.

Eu recomendo o TestDriven.Net ou ReSharper, pois ambos se integram facilmente ao Visual Studio.


1

Eu recomendaria escrever vários testes para os métodos Authenticate e Save. Além do caso de sucesso (onde todos os parâmetros são fornecidos, tudo está escrito corretamente, etc.), é bom ter testes para vários casos de falha (parâmetros incorretos ou ausentes, conexões indisponíveis do banco de dados, se aplicável, etc). Eu recomendo o Teste de unidade pragmática em C # com o NUnit como referência.

Como outros já declararam, os testes de unidade para getters e setters são um exagero, a menos que exista lógica condicional em seus getters e setters.


1

Embora seja possível adivinhar corretamente onde seu código precisa ser testado, geralmente acho que você precisa de métricas para fazer backup dessa suposição. Na minha opinião, o teste de unidade anda de mãos dadas com as métricas de cobertura de código.

Codifique com muitos testes, mas uma pequena cobertura não foi bem testada. Dito isto, o código com 100% de cobertura, mas não testando os casos de limite e erro, também não é ótimo.

Você deseja um equilíbrio entre alta cobertura (mínimo de 90%) e dados de entrada variáveis.

Lembre-se de testar "lixo"!

Além disso, um teste de unidade não é um teste de unidade, a menos que verifique uma falha. Os testes de unidade que não possuem afirmações ou estão marcados com exceções conhecidas simplesmente testam que o código não morre quando é executado!

Você precisa projetar seus testes para que eles sempre relatem falhas ou dados inesperados / indesejados!


1

Torna o nosso código melhor ... ponto final!

Uma coisa que nós, desenvolvedores de software, esquecemos ao fazer o desenvolvimento orientado a testes é o objetivo por trás de nossas ações. Se um teste de unidade estiver sendo gravado depois que o código de produção já estiver em vigor, o valor do teste será muito baixo (mas não será completamente perdido).

No verdadeiro espírito do teste de unidade, esses testes não existem principalmente para "testar" mais do nosso código; ou para obter uma cobertura de código 90% a 100% melhor. Esses são todos os benefícios adicionais de escrever os testes primeiro. A grande recompensa é que nosso código de produção acaba sendo gravado muito melhor devido ao processo natural do TDD.

Para ajudar a comunicar melhor essa idéia, o seguinte pode ser útil na leitura:

A teoria falha de testes de unidade
Desenvolvimento de software proposital

Se acharmos que o ato de escrever mais testes de unidade é o que nos ajuda a obter um produto de maior qualidade, podemos estar sofrendo de um culto à carga de desenvolvimento orientado a testes.


Discordo da afirmação de que os testes de unidade não têm valor após o código de produção já estar em vigor. Tais afirmações não são úteis para replicar condições de erro encontradas na produção ou no entendimento do código herdado de um desenvolvedor ou equipe anterior.
Scott Lawrence

Eu posso ter encontrado incorretamente. Não quis dizer que os testes de unidade não têm valor após o código de produção estar em vigor. No entanto, seu valor diminui. O maior benefício para os testes de unidade vem da magia inerente que ocorre quando deixamos que eles conduzam nosso desenvolvimento de produção.
Scott Saad
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.