Se un test unitario asserisce cosa una funzione non fa?

0

A volte vedo parti di codice in funzioni che sospetto potrebbero essere modificate in un modo specifico in seguito, interrompendo la sua funzionalità. Ad esempio, la funzione A potrebbe essere modificata per chiamare la funzione B, ma non dovrebbe.

Dovrebbe esserci un test unitario che afferma che la funzione A non chiama la funzione B?

Il caso a cui sto pensando è un componente dell'interfaccia utente in un'applicazione web che chiama un'API utilizzando le richieste Ajax, tramite un livello. In un certo caso dovrebbe chiamare una funzione in questo livello e, in un altro caso, una funzione diversa.

La conseguenza di chiamare quella sbagliata, nel mondo reale, è che fallisce in modo asincrono. È solo che userà un po 'di larghezza di banda, tempo del server e renderà difficile il debug dei problemi poiché sembra che qualcosa non funzioni durante l'ispezione del comportamento della rete. Finché a un certo punto viene chiamato quello corretto, il widget funziona come previsto.

    
posta Michal Charemza 08.09.2016 - 08:01
fonte

4 risposte

2

Idealmente, i test unitari dovrebbero verificare comportamento , non i dettagli di implementazione. Quindi verifichi che qualche input in A dia l'output atteso. Se qualcuno riscrive la funzione A per chiamare B, ma restituisce comunque l'output corretto, allora mostra solo che la chiamata B non è un problema dopo tutto, poiché la funzione funziona ancora.

    
risposta data 08.09.2016 - 08:40
fonte
2

La risposta breve è: dipende.

Questa domanda si applica davvero bene solo quando hai qualche tipo di opzioni di test comportamentali in atto. Ad esempio, quando si utilizza una struttura di simulazione. Quando si esegue il codice reale è davvero difficile testare che qualcosa non sia successo. Potresti fare affidamento su alcuni sgradevoli effetti collaterali, ma ciò rende il test altamente accoppiato e fragile.

Tuttavia, dato il corretto isolamento tramite mock / stub, ci sono certamente situazioni in cui si desidera verificare il comportamento corretto (invece del risultato corretto). E in alcuni casi, il comportamento corretto viene esplicitamente definito non facendo qualcosa.

Il test dovrebbe essere eseguito? Dipende davvero dalle conseguenze se si verifica il comportamento indesiderato. Di solito, se hai una buona ragione per dettare il tuo codice non per chiamare qualcosa, hai lo stesso buon motivo per testarlo.

Ecco alcuni esempi che evidenziano i casi in cui potresti volerlo fare e per quali motivi:

Cache: hai alcune funzionalità di cache, che possono restituire rapidamente risultati invece di costosi calcoli. Provando che il codice prende i risultati dalla cache e non esegue il calcolo ha molto senso.

Motivo decoratore: il motivo decoratore consente di decorare una classe e delegare tutte le chiamate di metodo a un'implementazione originale, tranne in quei luoghi in cui si desidera modificare il comportamento. Scrivi il decoratore perché desideri un comportamento diverso ed è un'altra ragione, in cui l'intenzione del tuo codice è già di non chiamare il metodo originale.

Filtri: a volte si attraversano raccolte, si filtrano determinati elementi e si esegue un'operazione su di essi. Verificare che il filtro sia corretto può essere fatto assicurando che l'operazione non sia eseguita sui rispettivi elementi.

    
risposta data 08.09.2016 - 09:17
fonte
2

In un contesto di sviluppo basato su test, i test unitari dovrebbero affermare ciò che viene specificato. Se un requisito vieta l'utilizzo di una funzione specifica, questo dovrebbe apparire in alcuni test di unità. Altrimenti, quel test non è necessario o le specifiche devono essere modificate.

    
risposta data 08.09.2016 - 10:33
fonte
0

Certo, puoi farlo ad esempio quando si effettuano 2 chiamate a Ajax si ottiene un risultato negativo. Ma il vero problema qui è la singola responsabilità:

function getVehicleName(type, id) {
  if(type === 'bike') {
    return getWithAjax('/bikes/' + id).name;
  }else{
    return getWithAjax('/cars/' + id).name;
  }
}

Un'alternativa sarebbe (molto semplice, può essere ulteriormente ottimizzata, se l'istruzione fuori rotta non è molto bella ma in questo caso semplice funziona):

function getVehicleName(type, id) {
  return getVehicle(type, id).name;
}

function getVehicle(type, id) {
  if(type === 'bike') {
    return getWithAjax('/bikes/' + id);
  }else{
    return getWithAjax('/cars/' + id);
  }
}

Può essere utile, anche più separato:

function getVehicleName(vehicleObj) {
  return vehicleObj;
}

function getVehicle(type, id) {
  if(type === 'bike') {
    return getWithAjax('/bikes/' + id);
  }else{
    return getWithAjax('/cars/' + id);
  }
}

//call:
alert(getVehicleName(getVehicle('car', 1)));

// optional helper:
function loadVehicleAndGetName(type, id) {
  return getVehicleName(getVehicle('car', 1));
}

//call:
alert(loadVehicleAndGetName('car', 1));

L'ultimo ha davvero tutte le responsabilità separate e ti permette di testare facilmente ciascuna di esse. Puoi prendere in giro getWithAjax ora anche molto semplice.

Quindi ora ha senso verificare che getVehicle chiami l'api giusta. Chiama solo una volta l'api. E questo alla fine risolve il tuo problema. Ora puoi rendere il comportamento di getVehicle così specifico che è molto semplice anche testare i limiti di ciò che non dovrebbe fare.

Quelli test sono interni? Non ne sono sicuro, penso che si tratti di testare il comportamento corretto: "Mi aspetto che ottenga quel veicolo una volta" e dal momento che si tratta di test unitari, penso che si adatti bene.

    
risposta data 08.09.2016 - 10:16
fonte

Leggi altre domande sui tag