Programmazione basata su contratto vs Unit Test


13

Sono un programmatore un po 'difensivo e un grande fan dei contratti di codice di Microsoft.

Ora non posso sempre usare C # e nella maggior parte delle lingue l'unico strumento che ho sono le asserzioni. Quindi di solito finisco con un codice come questo:

class
{       
    function()
    {   
         checkInvariants();
         assert(/* requirement */);

         try
         {
             /* implementation */
         }
         catch(...)
         {
              assert(/* exceptional ensures */);                  
         }
         finally
         {
              assert(/* ensures */);
              checkInvariants();
         }
    }

    void checkInvariants()
    {
         assert(/* invariant */);
    }
}

Tuttavia, questo paradigma (o come lo chiameresti) porta a un sacco di ingombri di codice.

Ho iniziato a chiedermi se ne sia valsa davvero la pena e se un corretto test unitario lo coprisse già?


6
Mentre i test unitari consentono di spostare le asserzioni fuori dal codice dell'applicazione (e quindi evitare l'ingombro), considera che non possono controllare tutto ciò che può accadere nel sistema di produzione reale. Quindi i contratti di codice IMO presentano alcuni vantaggi, in particolare per il codice critico in cui la correttezza è particolarmente importante.
Giorgio,

Quindi è sostanzialmente tempo di sviluppo, manutenibilità, leggibilità e migliore copertura del codice?
Ron

1
Per lo più sto usando l'asserzione nel codice per verificare la correttezza dei parametri (ad esempio su null) e nel test unitario verificare anche quelle asserzioni.
artjom,

Risposte:


14

Non penso che dovresti pensarlo come "vs".
Come menzionato nei commenti di @Giorgio, i contratti di codice devono controllare gli invarianti (nell'ambiente di produzione) e i test unitari devono assicurarsi che il codice funzioni come previsto quando tali condizioni sono soddisfatte.


2
Penso che sia anche importante verificare se il codice funziona (ad esempio genera un'eccezione) quando le condizioni non sono soddisfatte.
svick,

6

I contratti ti aiutano con almeno una cosa che i test unitari non fanno. Quando stai sviluppando un'API pubblica non puoi testare l'unità su come le persone usano il tuo codice. Puoi comunque definire contratti per i tuoi metodi.

Personalmente sarei così rigoroso riguardo ai contratti solo quando avrò a che fare con un'API pubblica di un modulo. In molti altri casi, probabilmente non vale la pena (e invece puoi usare unit test), ma questa è solo la mia opinione.

Ciò non significa che consiglio di non pensare ai contratti in quei casi. Ci penso sempre. Non penso sia necessario codificarli sempre esplicitamente.


1

Come già accennato, i contratti e i test unitari hanno scopi diversi.

I contratti riguardano la programmazione difensiva per assicurarsi che siano soddisfatti i prerequisiti, che il codice venga chiamato con i parametri corretti, ecc.

Test unitari per garantire che il codice funzioni, in diversi scenari. Queste sono come "specifiche con i denti".

Le affermazioni sono buone e rendono il codice robusto. Tuttavia, se sei preoccupato che si stia aggiungendo molto codice, potresti anche aggiungere punti di interruzione condizionali in alcuni punti durante il debug e ridurre il conteggio delle affermazioni.


0

Qualunque cosa tu abbia nelle tue chiamate checkVariants () potrebbe essere fatta dai test, quanto sforzo potrebbe effettivamente dipendere da molte cose (dipendenze esterne, livelli di accoppiamento ecc.) Ma ripulirebbe il codice da un punto di vista. Non è sicuro quanto sia testabile una base di codice sviluppata contro asserzioni senza un certo refactoring.

Sono d'accordo con @duros, questi non dovrebbero essere considerati approcci esclusivi o concorrenti. In effetti, in un ambiente TDD, si potrebbe persino sostenere che le affermazioni del "requisito" sarebbero necessarie per avere dei test :).

Tuttavia, le asserzioni NON rendono il codice più robusto a meno che tu non faccia effettivamente qualcosa per correggere il controllo fallito, impediscono semplicemente che i dati vengano danneggiati o simili, di solito interrompendo l'elaborazione al primo segno di problemi.

Una soluzione test driven / ben testata in genere ha già pensato e / o scoperto molte delle fonti / ragioni di input e output errati durante lo sviluppo dei componenti interagenti e li ha già affrontati più vicini alla fonte del problema.

Se la tua fonte è esterna e non hai alcun controllo su di essa, per evitare di ingombrare il tuo codice affrontando problemi con altri codici potresti prendere in considerazione l'implementazione di una sorta di componente di pulizia / asserzione dei dati tra la fonte e il tuo componente e mettere lì i tuoi controlli .

Inoltre sono curioso di sapere quali lingue stai usando che non hanno una sorta di xUnit o altra libreria di test che qualcuno ha sviluppato, ho pensato che ci fosse praticamente qualcosa per tutto in questi giorni?


0

Oltre ai test unitari e ai contratti di codice, ho pensato di evidenziare un altro aspetto, che è il valore nel definire le tue interfacce in modo da eliminare o ridurre la possibilità di chiamare il tuo codice in modo errato in primo luogo.

Non è sempre facile o possibile, ma sicuramente vale la pena porsi la domanda "Posso rendere questo codice più sicuro?"

Anders Hejlsberg, creatore di C #, ha affermato che uno dei maggiori errori in C # non includeva tipi di riferimento non annullabili. È uno dei motivi principali per cui esiste così tanto ingombro di codice di guardia necessario.

Tuttavia, il refactoring per avere solo la quantità necessaria e sufficiente di codice di protezione rende, nella mia esperienza, un codice più utilizzabile e gestibile.

Concordo con @duros sul resto.


0

Fai entrambe le cose, ma crea alcuni metodi di supporto statici per chiarire le tue intenzioni. Questo è ciò che Google ha fatto per Java, controlla code.google.com/p/guava-libraries/wiki/PreconditionsExplained

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.