Come impedire all'implementazione / all'algoritmo di passare ai test unitari?

1

Sto esitando tra algoritmo e implementazione poiché la maggior parte delle domande qui su come prevenire l'accoppiamento tra implementazione e test riguardano l'uso di spie / stub o mock.

Il problema tipico è che i test stanno semplicemente facendo il mirroring del SUT perché i test stanno concentrando la convalida del comportamento invece dello stato .

L'accoppiamento di cui sto parlando è dove i test stanno convalidando lo stato del SUT, ma devono rispecchiare il codice per raccogliere i dati necessari per formare l'asserzione.

Prendi ad esempio questo codice che verifica che il canvas abbia le dimensioni corrette:

it(
    'should set the width and height of the canvas', 
    fakeAsync(
        () => {
            fixture.detectChanges();
            getTestScheduler().flush();
            tick();

            let bordersWrapperStyle = getComputedStyle(fixture.nativeElement);
            let bordersWidth = bordersWrapperStyle
                .getPropertyValue('border-width')
                .split(' ');
            let yBordersTotalWidth = parseInt(bordersWidth[0]) * 2;
            let xBordersTotalWidth = parseInt(bordersWidth[1]) * 2;

            expect(page.canvasEl.width).toEqual(
                fixture.nativeElement.offsetWidth - xBordersTotalWidth
            );

            expect(page.canvasEl.height).toEqual(
                fixture.nativeElement.offsetHeight - yBordersTotalWidth
            );
        }
    )
);

Ora SUT:

setCanvasRect() {
    let rootEl = this.elRef.nativeElement;
    let bordersWrapperStyle = getComputedStyle(rootEl);
    let bordersWidth = bordersWrapperStyle.getPropertyValue('border-width').split(' ');
    let yBordersTotalWidth = parseInt(bordersWidth[0]) * 2;
    let xBordersTotalWidth = parseInt(bordersWidth[1]) * 2;

    this.canvasEl.width = rootEl.offsetWidth - xBordersTotalWidth;
    this.canvasEl.height = rootEl.offsetHeight - yBordersTotalWidth;
}

Forse la risposta è A volte è inevitabile avere questo tipo di accoppiamento tra test e SUT .

    
posta maximedupre 25.05.2018 - 00:39
fonte

4 risposte

8

Un modo per evitare questa duplicazione è creare un dispositivo con valori esplicitamente definiti. In questo esempio devi configurare il canvas con valori esplicitamente definiti per fixture.nativeElement.offsetWidth e xBordersTotalWidth . Quindi calcoli la differenza tra i due. La tua affermazione dovrebbe quindi verificare se i risultati attesi sono venuti fuori. Ciò eviterà la duplicazione che hai menzionato e quindi ti proteggerà anche dalla duplicazione dei calcoli errati nel codice di test (e quindi non testando veramente il risultato atteso). Ad esempio, se hai definito il tuo dispositivo in modo che fixture.nativeElement.offsetWidth fosse 310 e xBordersTotalWidth fosse 10, dovresti scrivere le tue aspettative come:

expect(page.canvasEl.width).toEqual(300);
    
risposta data 25.05.2018 - 01:15
fonte
3

Direi che il tuo problema è che setCanvasRect sta facendo due cose: sta entrambi analizzando una proprietà e impostando le proprietà sul canvas. Dividi l'analisi in una funzione / classe / qualsiasi, quindi puoi testare separatamente le due cose:

  1. Verifica che parseBorders(element) restituisca correttamente i bordi per l'elemento dato (potrebbe forse rompere questo ancor più nel prendere lo stile e analizzarlo).
  2. Verifica che setCanvasRect(w, h) imposti correttamente l'altezza e la larghezza.
  3. Verifica che setCanvasRect(element) compia le chiamate corrette a parseBorders(element) e setCanvasRect(w, h) , prendendo in giro le chiamate a parseBorders(element) e setCanvasRect(w, h) .
risposta data 25.05.2018 - 00:49
fonte
1

The coupling I am talking about is where tests are validating the state of the SUT, but have to mirror the code in order to gather the necessary data to form the assertion.

TL; DR: il termine di ricerca che desideri esplorare è property based testing .

Ciò che hai incontrato è un problema comune con i test basati su "esempi"; abbiamo un input noto, e quindi un output specifico che stiamo prendendo di mira, e se applichiamo abbastanza refactoring "remove dupation" nel codice di test, finiamo per scoprire l'implementazione che stiamo cercando di verificare.

I test basati sulle proprietà sono un'idea diversa: possiamo fare progressi con i test che controllano che le risposte abbiano le proprietà giuste, senza preoccuparsi troppo delle specifiche.

Scott Wlaschin ha scritto un'introduzione che mi piace molto, con i tentativi di scrivere test che costringono un avversario patologico a fornire una corretta implementazione di add(x,y) .

L'idea di base è di scavare intorno a cose che dovrebbero sempre essere vere: larghezza / altezza sempre di > = 0? saranno sempre meno delle estensioni dell'elemento nativo? la differenza sarà sempre un multiplo di due? Se l'altezza dell'elemento nativo è maggiore della larghezza, vuol dire che l'altezza della tela prevista è sempre maggiore della larghezza della tela prevista? e così via.

È in qualche modo analogo al trattamento delle post-condizioni nella progettazione per contratto; puoi verificare che il risultato dell'operazione soddisfi una collezione di invarianti?

    
risposta data 25.05.2018 - 06:30
fonte
1

Potresti anche estrarre la parte di calcolo in una funzione pura, approssimativamente in questo modo:

setCanvasRect() {
    let rootEl = this.elRef.nativeElement;
    let bordersWrapperStyle = getComputedStyle(rootEl);

    let widthAndHeight = calculateWidthAndHeight(
        bordersWrapperStyle.getPropertyValue('border-width'),
        rootEl.offsetWidth,
        rootEl.offsetHeight);

    this.canvasEl.width = widthAndHeight[0];
    this.canvasEl.height = widthAndHeight[1];
}

calculateWidthAndHeight(bordersWidth, rootOffsetWidth, rootOffsetHeight) {
    let bordersWidthElems = bordersWidth.split(' ')
    let yBordersTotalWidth = parseInt(bordersWidthElems[0]) * 2;
    let xBordersTotalWidth = parseInt(bordersWidthElems[1]) * 2;
    return [rootOffsetWidth - xBordersTotalWidth, rootOffsetHeight - yBordersTotalWidth];
}

Quindi puoi testare con esempi concreti

expect(calculateWidthAndHeight("10 5", 100, 200).toEqual([80, 190])
    
risposta data 25.05.2018 - 08:39
fonte

Leggi altre domande sui tag