Come fai a sapere cosa testare durante la scrittura di unit test? [chiuso]


127

Usando C #, ho bisogno di una classe chiamata Userche abbia un nome utente, password, flag attivo, nome, cognome, nome completo, ecc.

Dovrebbero esserci metodi per autenticare e salvare un utente. Scrivo solo un test per i metodi? E devo anche preoccuparmi di testare le proprietà in quanto sono getter e setter di .Net?


Questo post aiuterà a restringere la domanda più ampia: earnestengineer.blogspot.com/2018/03/… Puoi seguire queste linee guida per focalizzare la tua domanda
Lindsay Morsillo,

Ricordare che le password non devono essere archiviate come testo normale.
Mr Anderson,

Risposte:


131

Molte ottime risposte a questo sono anche sulla mia domanda: " Inizio TDD - Sfide? Soluzioni? Consigli? "

Vorrei anche raccomandare di dare un'occhiata al mio post sul blog (che è stato in parte ispirato dalla mia domanda), ho avuto un buon feedback su questo. Vale a dire:

Non so da dove iniziare?

  • Ricominciare. Pensa solo a scrivere test quando scrivi un nuovo codice. Può essere una rielaborazione del vecchio codice o una funzionalità completamente nuova.
  • Inizia semplice. Non scappare e cercare di aggirare la testa in un framework di test oltre ad essere TDD-esque. Debug.Assert funziona bene. Usalo come punto di partenza. Non scherza con il tuo progetto né crea dipendenze.
  • Inizia positivo. Stai cercando di migliorare il tuo mestiere, sentirti bene. Ho visto molti sviluppatori là fuori che sono felici di ristagnare e non provare nuove cose per migliorare se stessi. Stai facendo la cosa giusta, ricordalo e ti aiuterà a non arrenderti.
  • Inizia pronto per una sfida. È abbastanza difficile iniziare a provare. Aspettati una sfida, ma ricorda: le sfide possono essere superate.

Prova solo per quello che ti aspetti

Ho avuto problemi reali quando ho iniziato perché ero costantemente seduto lì a cercare di capire ogni possibile problema che potesse verificarsi e quindi provare a provarlo e risolverlo. Questo è un modo rapido per avere mal di testa. I test dovrebbero essere un vero processo YAGNI. Se sai che c'è un problema, allora scrivi un test per questo. Altrimenti, non preoccuparti.

Prova solo una cosa

Ogni caso di test dovrebbe testare solo una cosa. Se ti capita mai di inserire "e" nel nome del test case, stai facendo qualcosa di sbagliato.

Spero che questo significhi che possiamo passare da "getter e setter" :)


2
"Se sai che c'è un problema, allora scrivi un test per farlo. Altrimenti, non preoccuparti." Non sarei d'accordo con il modo in cui questo è formulato. Avevo l'impressione che i test unitari dovessero coprire tutti i possibili percorsi di esecuzione.
Corin Blaikie,

3
Mentre alcuni possono sostenere tali cose, io personalmente no. Un buon 90% del mio mal di testa è venuto semplicemente provando a fare "tutto". Dico test per quello che ti aspetti che accada (quindi sai che stai recuperando i valori giusti) ma non provare a capire tutto. YAGNI.
Rob Cooper,

4
Anch'io sostengo l'approccio "test your bugs". Se tutti avessimo tempo e pazienza infiniti, testeremmo ogni possibile percorso di esecuzione. Ma non lo facciamo, quindi devi spendere i tuoi sforzi dove avrà il massimo effetto.
Schwern,

63

Metti alla prova il tuo codice, non la lingua.

Un test unitario come:

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

è utile solo se stai scrivendo un compilatore e c'è una possibilità diversa da zero che il tuo instanceofmetodo non funzioni.

Non testare cose che puoi fare affidamento sulla lingua per far rispettare. Nel tuo caso, mi concentrerei sui tuoi metodi di autenticazione e di salvataggio - e scrivo test che assicurano che siano in grado di gestire i valori null in uno o tutti quei campi con grazia.


1
Un buon punto su "Non testare il framework" - Qualcosa su cui sono entrato anche io quando sono nuovo di questo. + 1'ed :)
Rob Cooper,

38

Questo mi ha portato a test unitari e mi ha fatto molto piacere

Abbiamo appena iniziato a fare test di unità. Per molto tempo ho saputo che sarebbe stato bello iniziare a farlo, ma non avevo idea di come iniziare e, cosa più importante, cosa testare.

Quindi abbiamo dovuto riscrivere un codice importante nel nostro programma di contabilità. Questa parte è stata molto complessa in quanto ha coinvolto molti scenari diversi. La parte di cui sto parlando è un metodo per pagare le vendite e / o acquistare fatture già inserite nel sistema contabile.

Non sapevo come iniziare a codificarlo, poiché c'erano così tante diverse opzioni di pagamento. Una fattura potrebbe essere $ 100 ma il cliente ha trasferito solo $ 99. Forse hai inviato fatture di vendita a un cliente ma hai anche acquistato da quel cliente. Quindi l'hai venduto per $ 300 ma hai comprato per $ 100. Puoi aspettarti che il tuo cliente ti paghi $ 200 per saldare il saldo. E se vendessi per $ 500 ma il cliente ti pagasse solo $ 250?

Quindi ho avuto un problema molto complesso da risolvere con molte possibilità che uno scenario funzionasse perfettamente ma sarebbe sbagliato su un altro tipo di combinazione invocie / pagamento.

È qui che i test unitari sono venuti in soccorso.

Ho iniziato a scrivere (all'interno del codice di prova) un metodo per creare un elenco di fatture, sia per le vendite che per gli acquisti. Quindi ho scritto un secondo metodo per creare il pagamento effettivo. Normalmente un utente inserisce tali informazioni tramite un'interfaccia utente.

Quindi ho creato il primo TestMethod, testando un pagamento molto semplice di una singola fattura senza sconti di pagamento. Tutte le azioni nel sistema si verificherebbero quando un pagamento bancario viene salvato nel database. Come puoi vedere, ho creato una fattura, creato un pagamento (una transazione bancaria) e salvato la transazione su disco. Nelle mie affermazioni ho inserito quelli che dovrebbero essere i numeri corretti che finiscono nella transazione bancaria e nella fattura collegata. Controllo il numero di pagamenti, gli importi del pagamento, l'importo dello sconto e il saldo della fattura dopo la transazione.

Dopo l'esecuzione del test, andrei nel database e ricontrollerei se quello che mi aspettavo fosse lì.

Dopo aver scritto il test, ho iniziato a codificare il metodo di pagamento (parte della classe BankHeader). Nella codifica mi sono solo preoccupato del codice per eseguire il primo test. Non pensavo ancora agli altri scenari più complessi.

Ho eseguito il primo test, risolto un piccolo bug fino a quando il mio test sarebbe passato.

Quindi ho iniziato a scrivere il secondo test, questa volta lavorando con uno sconto sul pagamento. Dopo aver scritto il test ho modificato il metodo di pagamento per supportare gli sconti.

Durante la verifica della correttezza con uno sconto sul pagamento, ho anche testato il pagamento semplice. Entrambi i test dovrebbero passare ovviamente.

Poi sono arrivato agli scenari più complessi.

1) Pensa a un nuovo scenario

2) Scrivi un test per quello scenario

3) Esegui quel singolo test per vedere se sarebbe passato

4) In caso contrario eseguirò il debug e modificherei il codice fino a quando non passerà.

5) Durante la modifica del codice ho continuato a eseguire tutti i test

È così che sono riuscito a creare il mio metodo di pagamento molto complesso. Senza test unitari non sapevo come iniziare la codifica, il problema sembrava travolgente. Con i test ho potuto iniziare con un metodo semplice ed estenderlo passo dopo passo con la certezza che gli scenari più semplici continuerebbero a funzionare.

Sono sicuro che l'uso del test unitario mi ha salvato alcuni giorni (o settimane) di codifica e garantisce più o meno la correttezza del mio metodo.

Se in seguito penso a un nuovo scenario, posso semplicemente aggiungerlo ai test per vedere se funziona o no. Altrimenti posso modificare il codice ma essere sicuro che gli altri scenari funzionino ancora correttamente. Ciò consentirà di risparmiare giorni e giorni nella fase di manutenzione e correzione degli errori.

Sì, anche il codice testato può avere ancora dei bug se un utente fa cose a cui non hai pensato o che gli ha impedito di fare

Di seguito sono riportati solo alcuni dei test che ho creato per testare il mio metodo di 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
Trovato un bug nel tuo test unitario! Ripeti questa riga invece di includere un 2 in uno di essi:Assert.AreEqual(0, bankHeader.BankCashDetails[0].Payments[3].InvoiceHeader.Balance);
Ryan Peschel,

2
Dici: "Dopo l'esecuzione del test, andrei nel database e ricontrollerei se quello che mi aspettavo fosse lì". Questo è un buon esempio di test di integrazione tra i componenti del sistema, non un test unitario isolato di un singolo pezzo di codice.
JTech,

2
Hai anche infranto la regola di più di un Assert per test.
Steve

13

Se sono davvero banali, non preoccuparti dei test. Ad esempio, se sono implementati in questo modo;

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

Se, d'altra parte, stai facendo qualcosa di intelligente, (come crittografare e decrittografare la password nel getter / setter), allora prova.


10

La regola è che devi testare ogni pezzo di logica che scrivi. Se hai implementato alcune funzionalità specifiche nei getter e nei setter penso che valga la pena testarli. Se assegnano valori solo ad alcuni campi privati, non preoccuparti.


6

Questa domanda sembra essere una questione di dove si disegna la linea su quali metodi vengono testati e quali no.

I setter e i getter per l'assegnazione di valore sono stati creati tenendo conto della coerenza e della crescita futura, e prevedendo che un po 'di tempo lungo il setter / getter possa evolversi in operazioni più complesse. Avrebbe senso mettere in atto test unitari di tali metodi, anche per motivi di coerenza e crescita futura.

L'obiettivo principale è l'affidabilità del codice, soprattutto durante le modifiche per aggiungere funzionalità aggiuntive. Non sono a conoscenza del fatto che qualcuno sia mai stato licenziato per aver incluso setter / getter nella metodologia di test, ma sono certo che esistano persone che desideravano avere metodi testati che alla fine erano a conoscenza o che potevano ricordare erano semplici set / get wrapper ma questo non era più a lungo il caso.

Forse un altro membro del team ha ampliato i metodi set / get per includere la logica che ora ha bisogno di essere testata ma non ha quindi creato i test. Ma ora il tuo codice chiama questi metodi e non sai che sono cambiati e hanno bisogno di test approfonditi e i test che fai in fase di sviluppo e QA non innescano il difetto, ma i dati aziendali reali il primo giorno di rilascio lo fanno innescalo.

I due compagni di squadra ora discuteranno su chi ha lasciato cadere la palla e non è riuscito a mettere i test unitari quando il set / viene trasformato per includere la logica che può fallire ma non è coperta da un test unitario. Il compagno di squadra che originariamente ha scritto il set / get sarà più facile uscire da questo clean se i test sono stati implementati dal primo giorno sul set / get semplice.

La mia opinione è che alcuni minuti di "spreco" di tempo che coprono TUTTI i metodi con test unitari, anche banali, potrebbero risparmiare giorni di mal di testa lungo la strada e perdita di denaro / reputazione dell'azienda e perdita del lavoro di qualcuno.

E il fatto che tu abbia avvolto metodi banali con unit test potrebbe essere visto da quel compagno di squadra junior quando cambiano i metodi banali in metodi non banali e li spingono ad aggiornare il test, e ora nessuno è nei guai perché il difetto era contenuto dal raggiungere la produzione.

Il modo in cui codifichiamo e la disciplina che può essere vista dal nostro codice possono aiutare gli altri.


4

Un'altra risposta canonica. Questo, credo, da Ron Jeffries:

Prova solo il codice che vuoi lavorare.


3

Testare il codice del boilerplate è una perdita di tempo, ma come dice Slavo, se aggiungi un effetto collaterale ai tuoi getter / setter, dovresti scrivere un test per accompagnare quella funzionalità.

Se stai eseguendo uno sviluppo guidato dai test, dovresti prima scrivere il contratto (ad es. L'interfaccia), quindi scrivere i test per esercitare quell'interfaccia che documenti i risultati / comportamento previsti. Quindi scrivi i tuoi metodi da soli, senza toccare il codice nei test delle unità. Infine, prendi uno strumento di copertura del codice e assicurati che i test esercitino tutti i percorsi logici nel codice.


3

Codici davvero banali come getter e setter che non hanno un comportamento aggiuntivo rispetto all'impostazione di un campo privato sono eccessivi da testare. In 3.0 C # ha anche un po 'di zucchero sintattico in cui il compilatore si occupa del campo privato, quindi non è necessario programmarlo.

Di solito scrivo molti test molto semplici per verificare il comportamento che mi aspetto dalle mie lezioni. Anche se è roba semplice come aggiungere due numeri. Passo molto tra la scrittura di un semplice test e la scrittura di alcune righe di codice. La ragione di ciò è che poi posso cambiare il codice senza temere di aver rotto cose a cui non pensavo.


Sono contento che tu abbia fatto un buon punto sul principio KISS .. Ho spesso dei test che sono letteralmente come 2-3 righe di codice, veri e propri piccoli test. Facile da brontolare e difficile da spezzare :) + 1'ed
Rob Cooper

3

Dovresti testare tutto. In questo momento hai getter e setter, ma un giorno potresti cambiarli un po ', magari per fare validazione o qualcos'altro. I test che scrivi oggi verranno utilizzati domani per assicurarsi che tutto continui a funzionare normalmente. Quando scrivi test, dovresti dimenticare considerazioni come "in questo momento è banale". In un contesto agile o guidato dai test, è necessario testare assumendo il refactoring futuro. Inoltre, hai provato a inserire valori davvero strani come stringhe estremamente lunghe o altri contenuti "cattivi"? Beh, non dovresti ... mai dare per scontato che il tuo codice possa essere abusato in futuro.

Generalmente trovo che scrivere test utente estesi sia da un lato, estenuante. D'altra parte, anche se ti dà sempre preziose informazioni su come dovrebbe funzionare la tua applicazione e ti aiuta a gettare via ipotesi facili (e false) (come: il nome utente avrà sempre una lunghezza inferiore a 1000 caratteri).


3

Per moduli semplici che possono finire in un toolkit o in un tipo di progetto open source, dovresti testare il più possibile includendo banali getter e setter. La cosa che devi tenere a mente è che generare un test unitario mentre scrivi un modulo particolare è abbastanza semplice e diretto. L'aggiunta di getter e setter è un codice minimo e può essere gestita senza pensarci troppo. Tuttavia, una volta che il codice è inserito in un sistema più grande, questo ulteriore sforzo può proteggerti dalle modifiche del sistema sottostante, come le modifiche al tipo in una classe base. Testare tutto è il modo migliore per avere una regressione completa.


2

Non fa male scrivere test unitari per i tuoi getter e setter. In questo momento, potrebbero semplicemente fare dei get / sets sul campo sotto il cofano, ma in futuro potresti avere una logica di validazione o dipendenze tra proprietà che devono essere testate. È più facile scriverlo ora mentre ci stai pensando, poi ricordarti di modificarlo se mai fosse il momento.


bene, se i tuoi getter / setter necessitano di unit test, ci deve essere una logica ad essi associata, quindi ciò significa che la logica deve essere scritta al loro interno, se non hanno alcuna logica, non è necessario scrivere unit test.
Pop Catalin,

2
Il suo punto è che la logica può essere aggiunta a loro in seguito.
LegendLength

2

in generale, quando un metodo è definito solo per determinati valori, verificare i valori al di sopra e al di là di ciò che è accettabile. In altre parole, assicurati che il tuo metodo faccia quello che dovrebbe fare, ma niente di più . Questo è importante, perché quando fallirai, vorrai fallire presto.

Nelle gerarchie ereditarie, assicurarsi di verificare la conformità LSP .

Testare getter e setter predefiniti non mi sembra molto utile, a meno che tu non abbia intenzione di fare qualche validazione in seguito.


2

bene se pensi che possa rompersi, scrivi un test per questo. Di solito non testare setter / getter, ma lasciami dire che ne fai uno per User.Name, che concatena nome e cognome, scrivo un test quindi se qualcuno cambia l'ordine per cognome e nome, almeno lo saprebbe ha cambiato qualcosa che è stato testato.


2

La risposta canonica è "testare qualsiasi cosa possa eventualmente rompersi". Se sei sicuro che le proprietà non si romperanno, non testarle.

E una volta trovato qualcosa che si è rotto (trovi un bug), ovviamente significa che devi testarlo. Scrivi un test per riprodurre il bug, guardalo fallire, quindi correggi il bug, quindi guarda il test passato.


1

Quando capisco i test unitari nel contesto dello sviluppo agile, Mike, sì, devi testare i getter e i setter (supponendo che siano pubblicamente visibili). L'intero concetto di unit test è quello di testare l'unità software, che in questo caso è una classe, come una scatola nera . Poiché i getter e i setter sono visibili esternamente, è necessario testarli insieme a Authenticate and Save.


1

Se i metodi Authenticate and Save utilizzano le proprietà, i test toccheranno indirettamente le proprietà. Finché le proprietà stanno solo fornendo accesso ai dati, non dovrebbero essere necessari test espliciti (a meno che non si stia andando per una copertura del 100%).


1

Vorrei testare i tuoi getter e setter. A seconda di chi sta scrivendo il codice, alcune persone cambiano il significato dei metodi getter / setter. Ho visto l'inizializzazione delle variabili e altre convalide come parte dei metodi getter. Per testare questo genere di cose, vorresti test unitari che coprono esplicitamente quel codice.


1

Personalmente "testerei tutto ciò che può rompersi" e il semplice getter (o anche le migliori proprietà automatiche) non si romperà. Non ho mai avuto una semplice dichiarazione di reso fallita e quindi non ho mai testato per loro. Se i getter hanno un calcolo al loro interno o qualche altra forma di affermazioni, aggiungerei sicuramente dei test per loro.

Personalmente uso Moq come un mock object framework e quindi verifico che il mio oggetto chiama gli oggetti circostanti come dovrebbe.


1

Devi coprire l'esecuzione di ogni metodo della classe con UT e controllare il valore restituito dal metodo. Ciò include getter e setter, specialmente nel caso in cui i membri (proprietà) siano classi complesse, che richiedono una grande allocazione di memoria durante la loro inizializzazione. Chiama il setter con una stringa molto grande, ad esempio (o qualcosa con simboli greci) e controlla che il risultato sia corretto (non troncato, la codifica è buona ecc.)

Nel caso di numeri interi semplici che si applicano - cosa succede se si passa lungo anziché intero? Questo è il motivo per cui scrivi UT per :)


1

Non testerei l'effettiva impostazione delle proprietà. Sarei più preoccupato di come queste proprietà vengono popolate dal consumatore e di cosa le popolano. Con qualsiasi test, devi soppesare i rischi con i tempi / i costi dei test.


1

Dovresti testare "ogni blocco di codice non banale" usando i test unitari per quanto possibile.

Se le tue proprietà sono banali ed è improbabile che qualcuno introduca un bug, allora dovrebbe essere sicuro non testarle.

I tuoi metodi Authenticate () e Save () sembrano buoni candidati per i test.


1

Idealmente, avresti fatto i test unitari mentre stavi scrivendo la lezione. Ecco come intendi farlo quando usi Test Driven Development. Aggiungete i test mentre implementate ciascun punto di funzione, assicurandovi di coprire anche i casi limite con test.

Scrivere i test in seguito è molto più doloroso, ma fattibile.

Ecco cosa farei nella tua posizione:

  1. Scrivi una serie base di test che testano la funzione principale.
  2. Ottieni NCover ed eseguilo sui tuoi test. La copertura del test sarà probabilmente intorno al 50% a questo punto.
  3. Continua ad aggiungere test che coprono i casi limite fino a ottenere una copertura di circa l'80% -90%

Questo dovrebbe darti una buona serie di test unitari che fungeranno da buon buffer contro le regressioni.

L'unico problema con questo approccio è che il codice deve essere progettato per essere testabile in questo modo. Se hai commesso errori di accoppiamento all'inizio, non sarai in grado di ottenere una copertura elevata molto facilmente.

Questo è il motivo per cui è davvero importante scrivere i test prima di scrivere il codice. Ti costringe a scrivere codice che è liberamente accoppiato.


1

Non testare ovviamente il codice di lavoro (boilerplate). Quindi, se setter e getter sono solo "propertyvalue = value" e "return propertyvalue" non ha senso testarlo.


1

Anche get / set può avere conseguenze strane, a seconda di come sono state implementate, quindi dovrebbero essere trattati come metodi.

Ogni test di questi dovrà specificare una serie di parametri per le proprietà, definendo proprietà accettabili e inaccettabili per garantire che le chiamate ritornino / falliscano nel modo previsto.

È inoltre necessario essere consapevoli dei gotcha di sicurezza, ad esempio un esempio di iniezione SQL, e testarli.

Quindi sì, devi preoccuparti di testare le proprietà.


1

Credo che sia sciocco testare getter e setter quando fanno solo un'operazione semplice. Personalmente non scrivo test unitari complessi per coprire qualsiasi modello di utilizzo. Provo a scrivere abbastanza test per assicurarmi di aver gestito il normale comportamento di esecuzione e quanti più casi di errore mi vengono in mente. Scriverò più unit test come risposta alle segnalazioni di bug. Uso il test unitario per garantire che il codice soddisfi i requisiti e per facilitare le modifiche future. Mi sento molto più disposto a cambiare codice quando so che se rompo qualcosa un test fallirà.


1

Scriverei un test per tutto ciò per cui stai scrivendo codice che è testabile al di fuori dell'interfaccia della GUI.

In genere, qualsiasi logica che scrivo che abbia una logica aziendale che inserisco in un altro livello o livello di logica aziendale.

Quindi scrivere test per tutto ciò che fa qualcosa è facile da fare.

Primo passaggio, scrivi un test unitario per ogni metodo pubblico nel tuo "Livello di logica aziendale".

Se avessi una lezione come questa:

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

        }

        public void CreditAccount(int accountNumber, double amount)
        {

        }

        public void CloseAccount(int accountNumber)
        {

        }
    }

La prima cosa che farei prima di scrivere qualsiasi codice sapendo che dovevo compiere queste azioni sarebbe iniziare a scrivere unit test.

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

        }

        [Test]
        public void CreditAccountTest()
        {

        }

        [Test]
        public void CloseAccountTest()
        {

        }
    }

Scrivi i tuoi test per convalidare il codice che hai scritto per fare qualcosa. Se stai iterando su una raccolta di cose e cambiando qualcosa su ciascuna di esse, scrivi un test che fa la stessa cosa e asserisci che è realmente accaduto.

Ci sono molti altri approcci che puoi adottare, vale a dire Behavoir Driven Development (BDD), che è più coinvolto e non è un ottimo punto di partenza con le tue abilità di testing delle unità.

Quindi, la morale della storia è, testare tutto ciò che fa qualcosa di cui potresti essere preoccupato, tenere i test unitari test di cose specifiche di piccole dimensioni, molti test sono buoni.

Mantieni la tua logica aziendale al di fuori del livello dell'interfaccia utente in modo da poter scrivere facilmente i test per loro e sarai bravo.

Consiglio TestDriven.Net o ReSharper in quanto entrambi si integrano facilmente in Visual Studio.


1

Consiglierei di scrivere più test per i tuoi metodi Authenticate and Save. Oltre al caso di successo (in cui vengono forniti tutti i parametri, tutto è scritto correttamente, ecc.), È bene avere test per vari casi di errore (parametri errati o mancanti, connessioni al database non disponibili se applicabile, ecc.). Raccomando Pragmatic Unit Testing in C # con NUnit come riferimento.

Come altri hanno affermato, i test unitari per getter e setter sono eccessivi, a meno che non vi sia una logica condizionale nei getter e nei setter.


1

Mentre è possibile indovinare correttamente dove il codice deve essere testato, in genere penso che siano necessarie metriche per eseguire il backup di questa ipotesi. A mio avviso, i test unitari vanno di pari passo con le metriche di copertura del codice.

Codice con molti test ma una piccola copertura non è stata ben testata. Detto questo, il codice con una copertura del 100% ma non testando i casi vari ed errori non è eccezionale.

Volete un equilibrio tra alta copertura (minimo 90%) e dati di input variabili.

Ricordati di provare "immondizia"!

Inoltre, un test unitario non è un test unitario a meno che non verifichi un errore. I test unitari che non hanno asserzioni o sono contrassegnati con eccezioni note testeranno semplicemente che il codice non muore durante l'esecuzione!

È necessario progettare i test in modo che segnalino sempre guasti o dati imprevisti / indesiderati!


1

Rende il nostro codice migliore ... periodo!

Una cosa che noi sviluppatori di software dimentichiamo quando facciamo lo sviluppo guidato dai test è lo scopo dietro le nostre azioni. Se un test unitario viene scritto dopo che il codice di produzione è già in atto, il valore del test diminuisce (ma non viene completamente perso).

Nel vero spirito di unit testing, questi test non sono principalmente lì per "testare" più del nostro codice; o per ottenere una copertura del codice migliore del 90% -100%. Questi sono tutti i vantaggi collaterali di scrivere prima i test. Il grande vantaggio è che il nostro codice di produzione finisce per essere scritto molto meglio a causa del processo naturale di TDD.

Per aiutare a comunicare meglio questa idea, potrebbe essere utile leggere quanto segue:

La teoria imperfetta dei test
unitari Sviluppo software mirato

Se riteniamo che l'atto di scrivere più unit test sia ciò che ci aiuta a ottenere un prodotto di qualità superiore, allora potremmo soffrire di un culto del carico di sviluppo guidato dai test.


Non sono d'accordo con l'affermazione che i test unitari non hanno valore dopo che il codice di produzione è già in atto. Tali asserzioni non tengono conto della loro utilità nel replicare le condizioni di errore riscontrate nella produzione o nella comprensione del codice ereditato da un precedente sviluppatore o team.
Scott Lawrence,

Potrei essermi imbattuto in modo errato. Non intendevo dire che i test unitari non hanno valore dopo che è stato inserito il codice di produzione. Tuttavia, il loro valore scende. Il più grande vantaggio dei test unitari deriva dalla magia intrinseca che si verifica quando lasciamo che guidino il nostro sviluppo della produzione.
Scott Saad,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.