Perché non testare una lingua come funzione supportata a livello di sintassi?

36

Puoi trovare un elenco infinito di blog, articoli e siti web che promuovono i vantaggi del test dell'unità del tuo codice sorgente. È quasi garantito che gli sviluppatori che hanno programmato i compilatori per Java, C ++, C # e altre lingue tipizzate hanno utilizzato il test delle unità per verificare il loro lavoro.

Allora perché, nonostante la sua popolarità, la sperimentazione è assente dalla sintassi di queste lingue?

Microsoft ha introdotto LINQ a C # , quindi perché non potrebbero aggiungere test?

Non sto cercando di prevedere quali sarebbero quei cambiamenti linguistici, ma per chiarire perché sono assenti all'inizio.

Ad esempio: sappiamo che puoi scrivere un ciclo for senza la sintassi dell'istruzione for . Puoi utilizzare while o if / goto istruzioni. Qualcuno ha deciso che una dichiarazione for era più efficiente e l'ha introdotta in una lingua.

Perché i test non hanno seguito la stessa evoluzione dei linguaggi di programmazione?

    
posta cgTag 03.07.2014 - 18:55
fonte

6 risposte

36

Come per molte cose, il test delle unità è supportato al massimo a livello di libreria, non a livello di linguaggio. In particolare, C # dispone di numerose librerie Unit Testing disponibili, nonché di elementi nativi di .NET Framework come Microsoft.VisualStudio.TestTools.UnitTesting .

Ogni libreria Test unità ha una filosofia e una sintassi di test un po 'diverse. A parità di condizioni, più scelte sono meglio di meno. Se i test delle unità sono stati integrati nella lingua, verrebbero bloccati nelle scelte del progettista del linguaggio, o useresti ... una libreria ed eviterai del tutto le funzionalità di test della lingua.

Esempi

  • Nunit : scopo generale, framework di test dell'unità progettato in modo idiomatico che sfrutta appieno le funzionalità linguistiche di C #.

  • Moq - Framework di simulazione che sfrutta appieno le espressioni lambda e gli alberi di espressione, senza una metafora di registrazione / riproduzione.

Ci sono molte altre scelte. Librerie come Microsoft Fakes possono creare mami "shims ..." che non richiedono di scrivere le tue classi usando interfacce o metodi virtuali.

Linq non è una funzione linguistica (nonostante il suo nome)

Linq è una funzionalità di libreria. Abbiamo un sacco di nuove funzionalità nel linguaggio C # stesso gratuitamente, come espressioni lambda e metodi di estensione, ma l'effettiva implementazione di Linq è in .NET Framework.

C'è dello zucchero sintattico che è stato aggiunto a C # per rendere le dichiarazioni di linq più pulite, ma non è richiesto lo zucchero per usare linq.

    
risposta data 03.07.2014 - 19:03
fonte
21

Ci sono molte ragioni. Eric Lippert ha dichiarato più volte che la ragione per cui feature X non è in C # è perché non è nel loro budget. I progettisti di linguaggi non hanno una quantità infinita di tempo e denaro per implementare le cose, e ogni nuova funzione ha i costi di manutenzione associati. Mantenere la lingua il più piccola possibile non è solo più semplice per i progettisti di linguaggi, ma è anche più facile per chiunque sia in grado di scrivere implementazioni e strumenti alternativi (es. IDE) Inoltre, quando qualcosa viene implementato in termini di linguaggio piuttosto che in parte, si ottiene portabilità gratuita. Se il testing delle unità viene implementato come una libreria, è sufficiente scriverlo una sola volta e funzionerà in qualsiasi implementazione conforme della lingua.

Vale la pena notare che D ha un supporto a livello di sintassi per i test delle unità . Non so perché hanno deciso di lanciarlo, ma vale la pena notare che D è pensato per essere un "linguaggio di programmazione di sistemi di alto livello". I progettisti volevano che fosse praticabile per il tipo di codice a basso livello non sicuro utilizzato tradizionalmente da C ++, e un errore nel codice non sicuro è incredibilmente costoso - comportamento indefinito. Quindi suppongo che avesse senso per loro fare uno sforzo extra su tutto ciò che ti aiuta a verificare che funzioni un codice non sicuro. Ad esempio, è possibile applicare il fatto che solo determinati moduli affidabili possono eseguire operazioni non sicure come accessi di array non selezionati o aritmetica del puntatore.

Lo sviluppo rapido era anche una priorità per loro, tanto che hanno fatto un obiettivo di progettazione che il codice D compili abbastanza velocemente da renderlo utilizzabile come linguaggio di scripting. L'unità di cottura esegue il test direttamente nella lingua in modo da poter eseguire i test semplicemente passando un flag in più al compilatore. "

Tuttavia, ritengo che una libreria di test delle unità grandi faccia molto di più che trovare solo alcuni metodi ed eseguirli. Prendi ad esempio il QuickCheck di Haskell, che ti permette di testare cose come "per tutti xey, f (x, y) == f (y, x) ". QuickCheck è meglio descritto come unit test generatore e ti permette di testare cose ad un livello più alto di "per questo input, mi aspetto questo output". QuickCheck e Linq non sono poi così diversi: sono entrambi linguaggi specifici del dominio. Quindi, piuttosto che imbullonare il supporto di test unitari in una lingua, perché non aggiungere le funzionalità necessarie per rendere i DSL pratici? Come risultato, non si verificheranno solo test delle unità, ma una lingua migliore.

    
risposta data 03.07.2014 - 20:35
fonte
13

Perché il testing, e in particolare lo sviluppo basato sui test, è un fenomeno profondamente controintuitivo.

Quasi tutti i programmatori iniziano la loro carriera credendo che siano molto più bravi a gestire la complessità di quanto non siano in realtà. Il fatto che anche il più grande programmatore non possa scrivere programmi grandi e complessi senza gravi errori a meno che non usino molti test di regressione è seriamente deludente e persino vergognoso per molti professionisti. Da qui la prevalente disinclinazione nei confronti dei test regolari anche tra i professionisti che dovrebbero già conoscersi meglio.

Penso che il fatto che i test religiosi stiano pian piano diventando più mainstream e attesi è in gran parte dovuto al fatto che con l'esplosione della capacità di memorizzazione e potenza di calcolo, i sistemi più grandi che mai sono in costruzione - e i sistemi estremamente grandi sono particolarmente inclini a crollo della complessità che non può essere gestito senza la rete di sicurezza dei test di regressione. Di conseguenza, anche gli sviluppatori particolarmente ostinati e illusi ora ammettono a malincuore che hanno bisogno di test e avranno sempre bisogno di test (se questo sembra la confessione a un meeting AA, questo è piuttosto intenzionale - la necessità di una rete di sicurezza è psicologicamente difficile da ammettere per molti individui).

La maggior parte delle lingue conosciute oggi risalgono a prima di questo cambio di atteggiamento, quindi hanno un piccolo supporto integrato per i test: hanno assert , ma nessun contratto. Sono abbastanza sicuro che se la tendenza persiste, le lingue future avranno più supporto sulla lingua piuttosto che sul livello di libreria.

    
risposta data 03.07.2014 - 21:25
fonte
6

Molte lingue hanno il supporto per i test. Le affermazioni di C sono test che il programma può fallire. È qui che la maggior parte delle lingue si interrompe, ma Eiffel e più recentemente Ada 2012 hanno preinvariants (cose che gli argomenti di una funzione devono passare) e postinvariants (cose che l'output di una funzione deve passare), con Ada che offre la possibilità di fare riferimento agli argomenti iniziali nella postinvariant. Ada 2012 offre anche invarianti di tipi, quindi ogni volta che un metodo viene chiamato su una classe Ada, il tipo invariante viene controllato prima del ritorno.

Questo non è il tipo di test completo che un buon framework di test può darti, ma è un tipo importante di test che le lingue possono supportare meglio.

    
risposta data 04.07.2014 - 01:32
fonte
2

Alcuni sostenitori di linguaggi funzionali strongmente tipizzati sostengono che queste caratteristiche linguistiche riducono o eliminano la necessità di test unitari.

Due, imho, un buon esempio di questo è da F # per Fun and Profit qui e qui

Personalmente, credo ancora nel valore dei test unitari, ma ci sono alcuni punti validi. Per esempio. se uno stato illegale non è rappresentabile nel codice, allora non è solo inutile scrivere un test unitario per questo caso, è impossibile.

    
risposta data 04.07.2014 - 01:13
fonte
2

Direi che ti sei perso l'aggiunta di alcune delle funzionalità necessarie perché non venivano evidenziate come per i test delle unità .

Ad esempio, il test unitario in C # è principalmente guidato dall'uso di attributi. La funzione Attributi personalizzati offre un ricco meccanismo di estensione che consente framework come NUnit per iterare e competere, con cose come basate sulla teoria e Parametrizzato test.

Questo mi porta al secondo punto principale: non ne sappiamo abbastanza su ciò che rende buona la testabilità in un linguaggio. Il ritmo dell'innovazione nei test è molto più rapido rispetto agli altri costrutti linguistici, quindi è necessario disporre di meccanismi flessibili nei nostri linguaggi per lasciare la libertà di innovare.

Sono un utente ma non fanatico di TDD - in alcuni casi è molto utile soprattutto per migliorare il tuo modo di progettare. Non è necessariamente utile per i sistemi legacy più grandi: test di livello superiore con buoni dati e automazione produrranno probabilmente più benefici insieme a una strong cultura di ispezione del codice .

Altre letture rilevanti:

risposta data 04.07.2014 - 03:45
fonte

Leggi altre domande sui tag