Test di una funzione che chiama solo una o due altre funzioni [duplicato]

0

La versione breve

Il codice

Come parte di TDD, spesso ci ritroviamo con funzioni che seguono questo schema:

function onSendRequestForSelected() {
    this.deselectStepsWithRequest();
    this.sendRequestForSelected();
}

Ciascuna delle funzioni chiamate internamente ha i propri test di unità.

I test

I nostri test di unità per questa funzione finiscono praticamente per garantire che siano state effettuate le chiamate al corpo della funzione:

expect( iStep.deselectStepsWithRequest ).toHaveBeenCalled();
expect( iStep.sendRequestForSelected ).toHaveBeenCalled();

Il problema

Il problema è:

  • I nostri test finiscono per testare chiamate di funzione anziché comportamento .
  • Non siamo sicuri di quale sia esattamente lo scopo di questi test: proteggono da nient'altro che da eventuali modifiche future.
  • Né servono come documentazione design .
  • Testano chiaramente come piuttosto che cosa .

La versione lunga

Punto di partenza

Iniziamo con il test:

it( 'should send a request for each of the selected steps', ... )

E poi implementa:

function onSendRequestForSelected() { ... }

Fin qui tutto bene.

Nuovo requisito, refactoring

Quindi ci rendiamo conto che è necessario deselezionare i passaggi che hanno già una richiesta.

Quindi refactor:

it( 'should send a request for each of the selected steps', ... )

diventa il test per

function sendRequestForSelected() { ... }

e crea

it( 'should deselect steps that already has a request', ... )

implementato da

function deselectStepsWithRequest() { ... }

Il test dei rifiuti

e quindi introdurre:

function onSendRequestForSelected() {
    this.deselectStepsWithRequest();
    this.sendRequestForSelected();
}

ma non ho idea di come testarlo.

it( 'should send a request for each of the selected steps', ... )

ha poco senso poiché il test stesso assicura solo che venga chiamata una funzione.

it( 'should call sendRequestForSelected()', ... )

sembra un test dei rifiuti.

La domanda

Quindi sembra che tu abbia due opzioni, nessuna è l'ideale. Qual è il migliore?

Dato TDD iniziamo con:

it( 'should deselect steps that already has a request', ... )
it( 'should send a request for each of the selected steps', ... )

function onSendRequestForSelected() {
    ...
}

Opzione 1

it( 'should deselect steps that already has a request', ... )
it( 'should send a request for each of the selected steps', ... )

function onSendRequestForSelected() {
    this.deselectStepsWithRequest();
    this.sendRequestForSelected();
}

I problemi:

  • Le funzioni secondarie attuali non sono test (se lo sarebbero sarebbero ridondanti). Pericoloso se verranno riutilizzati (considerarli necessari per altre funzioni).

Opzione 2

// No test

function onSendRequestForSelected() {
    // Test for each of the following
    this.deselectStepsWithRequest();
    this.sendRequestForSelected();
}

I problemi:

  • Questo non è veramente TDD; non è così che abbiamo iniziato.
  • Nessun test da utilizzare come documentazione di progettazione. Non definiamo il comportamento.

In forma grafica

Dove vengono rilasciati i test?

  • I test di F1 e F2 produrranno solo test ridondanti per S2.
  • I test per S1, S2 e S3 non testeranno il comportamento di F1 e F2.
posta Izhaki 18.11.2015 - 11:11
fonte

1 risposta

2

Questo è ovviamente un argomento controverso.

Dopo aver consultato una moltitudine di opinioni, la conclusione è che questo è corretto:

Dato il metodo

function onSendRequestForSelected();

e i suoi test di pre-implementazione:

it( 'should deselect steps that already has a request', ... )
it( 'should send a request for each of the selected steps', ... )

l'eventuale implementazione deve essere:

function onSendRequestForSelected() {
    this.deselectStepsWithRequest();
    this.sendRequestForSelected();
}

e le eventuali prove dovranno essere:

it( 'should deselect steps that already has a request', function(){
    expect( iStep.deselectStepsWithRequest ).toHaveBeenCalled();
});

it( 'should send a request for each of the selected steps', function(){
    expect( iStep.sendRequestForSelected ).toHaveBeenCalled();
});

Ragionamento

Gran parte del ragionamento alla base di questo è basato sulla mia argomentazione perché dovresti scrivere un test per ogni metodo (quasi), compresi setter e getter .

In breve:

  • TDD non riguarda solo la difesa, ma anche design e documentazione .
  • A fini di progettazione e documentazione, i test devono essere scritti prima dell'implementazione.
    • Questo è un principio fondamentale di TDD, quindi potrebbe sembrare elementare.
    • Ma la domanda che deve essere posta è: " Qualcuno può implementare questa parte del sistema in base ai test forniti ?".
    • Quindi, se non ci sono test, non ci sarà implementazione.
  • Il test è soggetto a refactoring e estrazioni così come lo è il codice di implementazione.

Estrazione test

Quindi, in pratica, la linea prevista qui:

it( 'should deselect steps that already has a request', function(){
    expect( iStep.deselectStepsWithRequest ).toHaveBeenCalled();
});

è un modo di dire:

The test condition, instead of being satisfied here will be satisfied by calling deselectStepsWithRequest()

(per il quale esiste un test separato).

È come l'estrazione del codice

molto in un modo this.deselectStepsWithRequest() qui:

function onSendRequestForSelected() {
    this.deselectStepsWithRequest();
}

è un modo di dire

Instead the execution code being here, it is in deselectStepsWithRequest().

    
risposta data 18.11.2015 - 15:48
fonte

Leggi altre domande sui tag