Come garantire che la funzionalità venga testata in modo coerente?

4

Come esempio , diciamo che sto lavorando alla creazione di una soluzione di e-commerce. Parte della funzionalità di questa soluzione sarà garantire che il prezzo finale di un ordine sia corretto. Diciamo che il prezzo finale includerà il totale complessivo di tutti i prodotti, i costi di spedizione e qualsiasi tassa.

Diciamo che ho due metodi pubblici:

/**
 * @return array
 */
public function viewAllOrders(){};

/**
 * @return array
 */
public function viewOrder($guid){};

Ogni metodo restituisce (tra le altre cose) il totale complessivo di un ordine.

Il problema che ho incontrato è che poiché ciascuno di questi metodi è testato in test separati, i test potrebbero avere definizioni diverse su quale dovrebbe essere il totale complessivo.

Ad esempio, diciamo per un ordine "A" il prezzo del prodotto è $ 100, spedizione $ 10, tasse $ 20 che danno un totale di $ 130.

Uno sviluppatore sta scrivendo il test per viewAllOrders . Scrive correttamente il test e afferma che per l'ordine 'A' il totale complessivo è $ 130.

Un altro sviluppatore scrive il test per viewOrder . Pensa che il totale generale è solo il prezzo per i prodotti e dimentica le tasse e le spese di spedizione. Ora afferma che per l'ordine "A" il totale complessivo è di $ 100.

Entrambi gli sviluppatori scrivono un codice di produzione che garantisce il superamento di tutti i test, ma ora abbiamo una situazione in cui l'ordine "A" ha un totale diverso a seconda del metodo pubblico che chiami! Gli errori si sono insinuati nel nostro sistema (anche se tutti i test stanno passando) perché due test non concordano sulla definizione del totale generale.

Quali sono alcuni buoni modi per assicurarci di non utilizzare definizioni diverse di funzionalità tra i nostri test?

Sto adottando il test "Classico / Statistico" per deridere. La ragione è che sto usando un linguaggio dinamico. Vedi I mock in lingue dinamiche sono pericolosi? . Ciò significa che non posso semplicemente avere un metodo grandTotal () che le altre classi chiamano e possono essere deriso nei test.

Ho anche considerato di testare ciascun metodo nello stesso test (garantendo così la definizione di un totale complessivo in un test). Il problema è che ti apri a Assertion Roulette .

Nota anche che ho banalizzato questo esempio. La maggior parte delle persone sa che cos'è il totale generale, ma il mio problema è generale, essendo spiegato con un esempio semplificato. Quando leggi "totale generale" questo può essere ugualmente letto come "un valore con un significato definito"

    
posta Gaz_Edge 29.07.2016 - 10:32
fonte

4 risposte

5

Assicurati di conoscere correttamente il tuo dominio e di progettarlo per questo.

In alcuni altri software, il calcolo di a+b potrebbe essere quasi irrilevante per il dominio. Nel tuo caso, stai parlando del dominio del commercio, in cui le tasse e la spedizione sono cose vitali. Pertanto, se un metodo fa semplicemente return cost + tax + shipping hai un difetto di progettazione orribile.

Questo è simile all'enfasi su contesto menzionato dall'altra risposta, ma trovo dominio più comprensibile. Perché è importante modellare correttamente il tuo dominio? Perché il tuo software è costruito e per questo.

Facciamo questo in modo più concreto: supponiamo che tu sia riuscito in qualche modo a garantire che i due metodi eseguano i calcoli in modo uguale, ma separatamente.

  • Che cosa farai quando scoprirai che l'aliquota d'imposta è diversa per ogni paese?
  • Come garantisci il corretto calcolo del prezzo per il cliente X super-importante che ottiene uno sconto del 3% su tutto ciò che ordina?
  • Come aggiusti i calcoli per la promozione della spedizione gratuita della prossima settimana?

Se sei nel business della vendita di materiale, il calcolo del prezzo è di importanza così vitale che avere due (e tanto più) posti nel tuo codice per decidere che diventa un grosso problema.

Ora, se dovessi scoprire una tale violazione, allora non vuoi semplicemente correggere i due calcoli per essere lo stesso. Vai alla causa alla base del problema e riprogetta il tuo sistema.

Potresti voler dare un'occhiata alla letteratura sul DDD (domain-driven design) su questo. La maggior parte di ciò ti aiuterà ad evitare tali problemi in un modo più generale (contesti limitati e linguaggio ubiquitario vengono in mente immediatamente).

    
risposta data 29.07.2016 - 15:37
fonte
1

Sembra un argomento semantico.

Uno dice:

order 'A' the product price is $100, shipping $10, tax $20 giving total price $130.

L'altro dice:

for order 'A' the total price is $100.

La differenza? Contesto.

Non hai una situazione in cui l'ordine A ha un prezzo totale diverso. Hai una situazione in cui qualsiasi ordine ha sia un totale parziale sia un totale complessivo che include cose oltre all'ordine A (spedizione, tasse). Se chiami entrambe queste cose in totale e ti aspetti che siano uguali è sciocco come se avessi passato all'ordine B e aspettavi che le cose fossero le stesse.

What are some good ways to ensure that we don't use different definitions of functionality across our tests?

Usa i buoni nomi. Rendi il contesto chiaro. Un test che solo l'autore del test comprende è un cattivo test. Total significa solo che alcune cose sono state aggiunte. Non è un nome molto utile. Dammi un suggerimento su ciò che è stato sommato. Il subtotale mi dice che questa è solo la prima ondata di totalizzazione. Grandtotal mi dice che questo è l'ultimo del totale.

Per garantire che il codice funzioni come previsto ed è comprensibile, è possibile avere revisioni tra pari e fare in modo che i peer scrivano i propri test su di esso. Un peer test mostra quanto bene il codice sia compreso in un modo così formale da essere compilato.

    
risposta data 29.07.2016 - 14:21
fonte
1
  • Crea un linguaggio condiviso tra tutti gli sviluppatori e gli esperti di business (vedi Ubiquitous Language in Domain Driven Design). Modella i concetti di dominio in modo preciso e inequivocabile attorno a tale linguaggio. L'autorità per il calcolo del prezzo dovrebbe essere in un posto centrale.

  • Poiché sembra che tu usi un approccio TDD Outside-in, quando ti avvicini al core layer (il dominio), avrai già i nomi degli oggetti del dominio grazie a quel modello. Il primo sviluppatore che ha bisogno di un determinato oggetto di dominio (o parte di esso) può implementarlo. I membri del team che ne avranno successivamente bisogno utilizzeranno tale implementazione.

(edit)

Questo potrebbe differire da un approccio "TDD come se si intendesse" estremo, in quanto non consiglio di rielaborare gli oggetti del dominio dal codice di produzione man mano che si procede. Invece, credo che quando raggiungi il limite del dominio, dovresti mettere in pausa il tuo test corrente e andare a creare un altro test unitario per l'oggetto dominio che ti serve. Implementalo completamente prima di tornare al test del livello esterno. A differenza del codice più tecnico, la modellazione di concetti e regole aziendali ricchi non può essere eseguita nel calore di una fase di refactoring di pochi minuti. Sono troppo importanti per essere improvvisati.

Come nota a margine, immagino che una pratica TDD più estremista tende a implicare la programmazione delle coppie lungo tutto e le coppie rotanti frequentemente, generando quindi molte meno opportunità per implementazioni in conflitto / duplicate. Che potrebbe funzionare altrettanto bene.

    
risposta data 29.07.2016 - 16:26
fonte
1

Requisiti del progetto, il codice e i relativi test unitari possono essere confusi quando hai sottolineato due funzioni che dovrebbero restituire lo stesso valore ma sono state interpretate in modo diverso dai programmatori.

Speriamo che ogni test unitario abbia qualche regola aziendale per giustificare la sua esistenza. Una revisione del codice può capirlo, ma in questo progetto sei passato.

Il test finale dell'utente dovrebbe essere dove questo viene trovato. Quando hai a che fare con i soldi degli altri, è meglio avere dei tester che sappiano cosa stanno facendo. Una cosa è confrontare le cose che dovrebbero essere le stesse. Respingo sempre le richieste che sembrano interpretare in modo leggermente diverso lo stesso valore. Non voglio che nessuno guardi due rapporti e mi chieda perché i "Big Bad Total" non corrispondono.

Una volta rilevata questa discrepanza, potresti passare attraverso il codice e trovare due funzioni che operano in base a diverse ipotesi su come calcolare qualcosa. Non si sa mai, ci può essere un posto dove è richiesta una funzione diversa. Un piccolo refactoring dovrebbe rendere un po 'più chiaro il nome di questa funzione e assicurarti di chiamarla solo da luoghi appropriati.

Il modo migliore per evitare questi problemi è quello di farlo bene la prima volta, ma raramente accade nei grandi progetti. Devi essere diligente e avere molti livelli di test. Non saranno tutti automatizzati, quindi le risorse devono essere lì o ti mancheranno le cose.

    
risposta data 29.07.2016 - 20:24
fonte

Leggi altre domande sui tag