Hyrndur: Að prófa async efni á fakedAsync svæðinu VS. útvega sérsniðna tímaáætlun

Mér hefur margoft verið spurt um „falsa svæðið“ og hvernig á að nota það. Þess vegna ákvað ég að skrifa þessa grein til að deila athugunum mínum þegar kemur að fínkornuðum „fakeAsync“ prófum.

Svæðið er áríðandi hluti vistkerfisins Hyrnds. Maður gæti hafa lesið að svæðið sjálft sé bara eins konar „framkvæmdarsamhengi“. Reyndar, hyrndur öpum saman alþjóðlegu aðgerðirnar eins og setTimeout eða setInterval til að stöðva aðgerðir sem eru framkvæmdar eftir nokkurn seinkun (setTimeout) eða reglulega (setInterval).

Það er mikilvægt að nefna að þessi grein er ekki að fara að sýna hvernig á að bregðast við setTimeout járnsögunum. Þar sem Angular nýtir RxJs mikið hvað byggir á tímasettum aðgerðum (þú gætir verið hissa en það er satt) notar það svæði sem flókið en öflugt tæki til að skrá allar ósamstilltar aðgerðir sem gætu haft áhrif á stöðu forritsins. Hyrndur hlerar þá til að vita hvort enn sé einhver vinna í biðröðinni. Það tæmir biðröðina eftir tíma. Líklega breyta tæmdu verkefnin gildi íhlutabreytanna. Fyrir vikið verður sniðmátið endurtekið.

Nú, allt óheilsusamlegt efni er ekki það sem við þurfum að hafa áhyggjur af. Það er bara gaman að átta sig á því sem gerist undir hettunni því það hjálpar til við að skrifa árangursrík einingapróf. Ennfremur hefur reynsludreifð þróun mikil áhrif á frumkóðann („Uppruni TDD var löngun til að fá sterkar sjálfvirkar aðhvarfsprófanir sem studdu þróunarhönnun. Á leiðinni sem iðkendur þess uppgötvuðu að skrifpróf fyrst gerðu verulegar umbætur á hönnunarferlinu. „Martin Fowler, https://martinfowler.com/articles/mocksArentStubs.html, 09/2017).

Sem afleiðing af allri þessari viðleitni getum við breytt tíma eins og við þurfum að prófa fyrir ástand á ákveðnum tímapunkti.

falsAsync / merkja yfirlit

The Angular docs fullyrðir að falsa Synchronic (https://angular.io/guide/testing#fake-async) gefi upp línulegri kóðunarupplifun vegna þess að hún losnar við loforð eins og .whenStable (). Þá (...).

Kóðinn í fakeAsync reitnum lítur svona út:

merkið (100); // bíðið eftir fyrsta verkefninu
fastur búnaður.detectChanges (); // uppfæra sýn með tilvitnun
merkið (); // bíðið eftir að annað verkið ljúki
fastur búnaður.detectChanges (); // uppfæra sýn með tilvitnun

Eftirfarandi smáútgáfur gefa innsýn í hvernig falið Synchronic virkar.

notuð er setTimeout / setInterval hér vegna þess að þau sýna greinilega hvenær aðgerðirnar eru keyrðar á fakeAsync svæðinu. Þú gætir búist við því að þessi „það“ aðgerð verði að vita hvenær prófið er gert (í Jasmine raðað eftir rifrildi: Aðgerð) en að þessu sinni reiðum við okkur á fakeAsync félaga frekar en að nota einhvers konar svarhringingu:

það ('tæmir svæðið verkefni eftir verkefni', fakeAsync (() => {
        setTimeout (() => {
            láta i = 0;
            const handle = setInterval (() => {
                ef (i ++ === 5) {
                    clearInterval (höndla);
                }
            }, 1000);
        }, 10000);
}));

Það kvartar hátt vegna þess að enn eru einhverjir „tímamælar“ (= setTimeouts) í biðröðinni:

Villa: 1 tímamæli (r) enn í biðröð.

Það er augljóst að við þurfum að breyta tímanum til að fá tímafrestinn. Við bætum breytu „merkið“ með 10 sekúndum:

merkið (10000);

Hugh? Villan verður ruglandi. Nú mistakast prófið vegna ályktana „reglubundinna tímamæla“ (= setIntervals):

Villa: 1 reglulega tímamæli (r) sem er enn í biðröð.

Þar sem við gerðum ráð fyrir aðgerð sem þarf að framkvæma á hverri sekúndu þurfum við líka að skipta um tíma með því að nota merkið aftur. Aðgerðin lýkur upp eftir 5 sekúndur. Þess vegna þurfum við að bæta við 5 sekúndum til viðbótar:

merkið (15000);

Nú líður prófið. Það er vert að segja að svæðið þekkir verkefni sem eru samhliða. Lengdu bara tímastillingu með öðru setInterval símtali.

það ('tæmir svæðið verkefni eftir verkefni', fakeAsync (() => {
    setTimeout (() => {
        láta i = 0;
        const handle = setInterval (() => {
            ef (++ i === 5) {
                clearInterval (höndla);
            }
        }, 1000);
        láta j = 0;
        const handle2 = setInterval (() => {
            ef (++ j === 3) {
                clearInterval (handle2);
            }
        }, 1000);
    }, 10000);
    merkið (15000);
}));

Prófið stendur enn yfir vegna þess að báðir þessir setIntervals voru byrjaðir á sama augnabliki. Báðir eru þeir búnir þegar 15 sekúndur eru liðnar:

fakeAsync / merkið í aðgerð

Nú vitum við hvernig fakeAsync / tick efnið virkar. Láttu það nota fyrir eitthvað þroskandi efni.

Við skulum þróa tillögu svipað svið sem uppfyllir þessar kröfur:

  • það grípur niðurstöðuna úr einhverju API (þjónustu)
  • það gerir notandinn kleift að bíða loka leitarorðsins (það fækkar beiðnum); DEBOUNCING_VALUE = 300
  • það sýnir niðurstöðuna í HÍ og sendir frá sér viðeigandi skilaboð
  • einingaprófið virðir ósamstilltur eðli kóðans og prófar rétta hegðun þess sviðs sem bendir til þess hvað varðar tímann

Við endum á þessum prófum:

lýsa ('í leit', () => {
    það ('hreinsar fyrri niðurstöðu', fakeAsync (() => {
    }));
    það ('gefur frá sér upphafsmerkið', fakeAsync (() => {
    }));
    það ('er hægt að slá inn mögulega hits af API til 1 beiðni á DEBOUNCING_VALUE millisekúndur', fakeAsync (() => {
    }));
});
lýsa ('on success', () => {
    það ('kallar Google API', fakeAsync (() => {
    }));
    það ('gefur frá sér árangursmerki með fjölda eldspýna', fakeAsync (() => {
    }));
    það ('sýnir titla í uppástunga reitnum', fakeAsync (() => {
    }));
});
lýsa ('á villu', () => {
    það ('gefur frá sér villumerkið', fakeAsync (() => {
    }));
});

Í „við leit“ bíðum við ekki eftir niðurstöðunni. Þegar notandinn gefur inntak (til dæmis „Lon“) þarf að hreinsa fyrri valkosti. Við reiknum með að möguleikar séu tómir. Að auki þarf að flýta fyrir inntaki notenda, svo að segja um 300 millisekúndur. Hvað varðar svæðið er 300 millis míkróataski ýtt út í biðröð.

Athugið að ég sleppi nokkrum smáatriðum:

  • prufuuppsetningin er nokkurn veginn sú sama og sést í Angular skjölum
  • apiService tilvikinu er sprautað í gegnum fixture.debugElement.injector (…)
  • SpecUtils kallar á atburði notenda eins og inntak og fókus
beforeEach (() => {
    spyOn (apiService, 'query'). and.returnValue (Observable.of (queryResult));
});
passa ('hreinsar fyrri niðurstöðu', fakeAsync (() => {
    comp.options = ['ekki tómt'];
    SpecUtils.focusAndInput ('Lon', fastur búnaður, 'inntak');
    merkið (DEBOUNCING_VALUE);
    fastur búnaður.detectChanges ();
    búast við (comp.options.length) .toBe (0, `var [$ {comp.options.join (',')}]`);
}));

Íhlutakóðinn sem reynir að fullnægja prófinu:

ngOnInit () {
    this.control.valueChanges.debounceTime (300). áskrift (gildi => {
        this.options = [];
        this.suggest (gildi);
    });
}
stinga upp á (q: streng) {
    this.googleBooksAPI.query (q). áskrift (result => {
// ...
    }, () => {
// ...
    });
}

Við skulum fara í gegnum kóðann skref fyrir skref:

Við njósnum um apiService fyrirspurnaraðferðina sem við ætlum að kalla íhlutinn. Breytanleg fyrirspurnResult inniheldur nokkur spotta gögn eins og „Hamlet“, „Macbeth“ og „King Lear“. Í byrjun gerum við ráð fyrir að valkostirnir verði tómir en eins og þú gætir hafa tekið eftir að öll falsa Synchronous biðröð tæmist með merki (DEBOUNCING_VALUE) og því inniheldur íhlutinn lokaútkomu skrifa Shakespeares líka:

Reiknað með að 3 væru 0, ‘var [Hamlet, Macbeth, Lear Lear].

Við þurfum seinkun á þjónustubeiðninni til að líkja eftir ósamstilltum tíma sem API-símtalið tekur. Við skulum bæta við 5 sekúndna seinkun (REQUEST_DELAY = 5000) og merkið við (5000).

beforeEach (() => {
    spyOn (apiService, 'query'). and.returnValue (Observable.of (queryResult). Delay (1000));
});

passa ('hreinsar fyrri niðurstöðu', fakeAsync (() => {
    comp.options = ['ekki tómt'];
    SpecUtils.focusAndInput ('Lon', fastur búnaður, 'inntak');
    merkið (DEBOUNCING_VALUE);
    fastur búnaður.detectChanges ();
    búast við (comp.options.length) .toBe (0, `var [$ {comp.options.join (',')}]`);
    merkið (REQUEST_DELAY);
}));

Að mínu mati ætti þetta dæmi að virka en Zone.js heldur því fram að enn sé einhver vinna í biðröðinni:

Villa: 1 reglulega tímamæli (r) sem er enn í biðröð.

Á þessum tímapunkti verðum við að fara dýpra í að sjá þær aðgerðir sem okkur grunar að festist á svæðinu. Að stilla nokkrar tímamót er leiðin:

kembiforrit fakeAsync svæði

Síðan skaltu gefa þetta út á skipanalínunni

_fakeAsyncTestZoneSpec._scheduler._schedulerQueue [0] .args [0] [0]

eða skoðaðu innihald svæðisins svona:

hmmm, skyndiaðferð AsyncScheduler er ennþá í biðröð ... af hverju?

Nafn aðgerðarinnar sem verið er að búa til er flushaðferð AsyncScheduler.

opinber flush (aðgerð: AsyncAction ): ógilt {
  const {aðgerðir} = þetta;
  ef (this.active) {
    aðgerðir. ýta (aðgerð);
    snúa aftur;
  }
  láta villa: allir;
  this.active = satt;
  gera {
    ef (error = action.execute (action.state, action.delay)) {
      brjóta;
    }
  } while (aðgerð = aðgerðir. breyting ()); // þreyttu tímaröð biðröðarinnar
  this.active = ósatt;
  ef (villa) {
    meðan (action = actions.shift ()) {
      aðgerð.afskráðu þig ();
    }
    kastvilla;
  }
}

Nú gætirðu velt því fyrir þér hvað er athugavert við frumkóðann eða svæðið sjálft.

Vandamálið er að svæðið og merkin okkar eru ekki samstillt.

Svæðið sjálft er með núverandi tíma (2017) en merkið vill afgreiða aðgerðina sem áætluð er 01.01.1970 + 300 millis + 5 sekúndur.

Gildi async tímaáætlunarinnar staðfestir að:

flytja {async sem AsyncScheduler} frá 'rxjs / scheduler / async';
// setja þetta einhvers staðar inni í „það“
console.info (AsyncScheduler.now ());
// → 1503235213879

AsyncZoneTimeInSync verndari til bjargar

Ein möguleg leiðrétting fyrir þetta er að hafa tól til að halda í samstillingu eins og þessa:

flytja flokk AsyncZoneTimeInSyncKeeper {
    tími = 0;
    framkvæmdaaðili () {
        spyOn (AsyncScheduler, 'núna'). og.callFake (() => {
            / * tslint: slökkva á næstu línu * /
            console.info ('tími', this.time);
            skila þessu.tíma;
        });
    }
    merkið (tími ?: tala) {
        ef (typeof tími! == 'óskilgreint') {
            this.time + = tími;
            merkið (this.time);
        } Annar {
            merkið ();
        }
    }
}

Það heldur utan um núverandi tíma sem nú er skilað () hvenær sem er kallaður á samstillingaráætlun. Þetta virkar vegna þess að auðkýfingin () notar sama núverandi tíma. Bæði, tímaáætlunin og svæðið, deila sama tíma.

Ég mæli með því að samstilla tímannInSync Keeper í fyrri hvers áfanga:

lýsa ('í leit', () => {
    láta tímaInSyncKeeper;
    beforeEach (() => {
        timeInSyncKeeper = nýr AsyncZoneTimeInSyncKeeper ();
    });
});

Nú skulum við líta á notkun tímastillingarforingjans. Hafðu í huga að við verðum að takast á við þetta tímasetningarmál vegna þess að textareiturinn er lagður af stað og beiðnin tekur nokkurn tíma.

lýsa („í leit“, () => {
    láta tímaInSyncKeeper;
    beforeEach (() => {
        timeInSyncKeeper = nýr AsyncZoneTimeInSyncKeeper ();
        spyOn (apiService, 'query'). and.returnValue (Observable.of (queryResult). Delay (REQUEST_DELAY));
    });
    það ('hreinsar fyrri niðurstöðu', fakeAsync (() => {
        comp.options = ['ekki tómt'];
        SpecUtils.focusAndInput ('Lon', fastur búnaður, 'inntak');
        timeInSyncKeeper.tick (DEBOUNCING_VALUE);
        fastur búnaður.detectChanges ();
        búast við (comp.options.length) .toBe (0, `var [$ {comp.options.join (',')}]`);
        timeInSyncKeeper.tick (REQUEST_DELAY);
    }));
    // ...
});

Við skulum fara í gegnum þetta dæmi línu fyrir línu:

  1. gera samstundis dæmi um samstillingarvörðinn
timeInSyncKeeper = nýr AsyncZoneTimeInSyncKeeper ();

2. láttu svara aðferðinni apiService.query með niðurstöður fyrirspurnar niðurstöðu eftir að REQUEST_DELAY er liðinn. Segjum að fyrirspurnaraðferðin sé hæg og svarar eftir REQUEST_DELAY = 5000 millisekúndur.

spyOn (apiService, 'query'). and.returnValue (Observable.of (queryResult). Delay (REQUEST_DELAY));

3. Láttu eins og það sé valkostur ‚ekki tómur‘ til staðar í uppástunga reitnum

comp.options = ['ekki tómt'];

4. Fara í „inntak“ reitinn í innbyggða hlutanum í búnaðinum og settu inn gildið „Lon“. Þetta líkir eftir samspili notenda við innsláttarsviðið.

SpecUtils.focusAndInput ('Lon', fastur búnaður, 'inntak');

5. láttu fara DEBOUNCING_VALUE tímabilið á fölsuðu async svæðinu (DEBOUNCING_VALUE = 300 millisekúndur).

timeInSyncKeeper.tick (DEBOUNCING_VALUE);

6. Finndu breytingar og endurfluttu HTML sniðmátið.

fastur búnaður.detectChanges ();

7. Valmöguleikinn er tómur núna!

búast við (comp.options.length) .toBe (0, `var [$ {comp.options.join (',')}]`);

Þetta þýðir að hægt var að keyra á sýnilegu gildi Skiptingum sem notaðir voru í íhlutunum á réttum tíma. Athugaðu að keyrð debounceTime-d virka

gildi => {
    this.options = [];
    this.onEvent.emit ({merki: SuggestSignal.start});
    this.suggest (gildi);
}

ýtti öðru verkefni í biðröð með því að kalla aðferðina benda til:

stinga upp á (q: streng) {
    ef (! q) {
        snúa aftur;
    }
    this.googleBooksAPI.query (q). áskrift (result => {
        ef (niðurstaða) {
            this.options = result.items.map (item => item.volumeInfo);
            this.onEvent.emit ({merki: SuggestSignal.success, totalItems: result.totalItems});
        } Annar {
            this.onEvent.emit ({merki: SuggestSignal.success, totalItems: 0});
        }
    }, () => {
        this.onEvent.emit ({merki: SuggestSignal.error});
    });
}

Mundu bara að njósnara á Google fyrirspurn API aðferð fyrirspurn sem svarar eftir 5 sekúndur.

8. Að lokum verðum við að merkja aftur fyrir REQUEST_DELAY = 5000 millisekúndur til að skola svæði í biðröð. Sá sýnilegi sem við gerum áskrift að í uppástunguaðferðinni þarf REQUEST_DELAY = 5000 til að klára.

timeInSyncKeeper.tick (REQUEST_DELAY);

fakeAsync…? Af hverju? Það eru tímasetningar!

Sérfræðingarnir frá ReactiveX gætu haldið því fram að við gætum notað tímasetningar prófa til að gera athuganlegar prófanir. Það er mögulegt fyrir Angular forrit en það hefur nokkra ókosti:

  • það krefst þess að þú kynnist innri uppbyggingu sýnilegra, rekstraraðila, ...
  • hvað ef þú ert með einhverja ljóta setTimeout lausn í umsókninni? Þeir eru ekki meðhöndlaðir af tímaáætlunum.
  • það mikilvægasta: Ég er viss um að þú vilt ekki nota tímaáætlun í öllu forritinu þínu. Þú vilt ekki blanda framleiðslukóðanum við einingaprófin þín. Þú vilt ekki gera eitthvað svona:
const testScheduler;
ef (umhverfi.test) {
    testScheduler = nýr YourTestScheduler ();
}
láta sjáanlegt;
ef (testScheduler) {
    obserable = Observable.of (‘gildi’). töf (1000, testScheduler)
} Annar {
    obserable = Observable.of (‘gildi’). töf (1000);
}

Þetta er ekki raunhæf lausn. Að mínu mati er eina mögulega lausnin að „sprauta“ prufuforritaranum með því að bjóða upp á „næstur“ fyrir hinar raunverulegu Rxjs aðferðir. Annað sem þarf að taka tillit til er að hnekktar aðferðir gætu haft neikvæð áhrif á einingaprófin sem eftir eru. Þess vegna ætlum við að nota njósnara Jasmine. Njósnarar verða hreinsaðir eftir hvert það.

Aðgerðin monkeypatchScheduler vefur upprunalegu Rxjs útfærsluna með njósnara. Njósnarinn tekur rök aðferðarinnar og bætir testScheduler ef við á.

flytja {IScheduler} frá 'rxjs / Tímaáætlun';
flytja {Athuganlegt} frá 'rxjs / Athuganlegt';
lýsa yfir var spyOn: Virka;
útflutningsaðgerð monkeypatchScheduler (tímaáætlun: IScheduler) {
    láta observableMethods = ['concat', 'fresta', 'tómt', 'forkJoin', 'ef', 'bil', 'sameina', 'af', 'svið', 'kasta',
        'zip'];
    láta operatorMethods = ['biðminni', 'concat', 'seinka', 'greinilegt', 'gera', 'hvert', 'síðast', 'sameinast', 'max', 'taka',
        'timeInterval', 'lift', 'debounceTime'];
    láttu injectFn = virka (grunn: allir, aðferðir: strengur []) {
        Method.forEach (aðferð => {
            const orig = stöð [aðferð];
            if (typeof orig === 'fall') {
                spyOn (grunn, aðferð) .og.callFake (fall () {
                    láta args = Array.prototype.slice.call (rök);
                    if (args [args.length - 1] && typeof args [args.length - 1] .now === 'fall') {
                        args [args.length - 1] = tímaáætlun;
                    } Annar {
                        args.push (tímaáætlun);
                    }
                    skila orig.apply (þetta, heldur því fram);
                });
            }
        });
    };
    injectFn (Observable, obserableMethods);
    injectFn (Observable.prototype, operatorMethods);
}

Héðan í frá mun testScheduler framkvæma alla vinnu í Rxjs. Það notar hvorki setTimeout / setInterval né einhvers konar async efni. Það er engin nauðsyn á fakeAsync lengur.

Nú þurfum við dæmi af tímasetningarprófi sem við viljum fara í monkeypatchScheduler.

Það hegðar sér mjög eins og sjálfgefna TestScheduler en það veitir svarhringingaraðferð onAction. Þannig vitum við hvaða aðgerðir voru framkvæmdar eftir hvaða tíma.

útflutningsflokkur SpyingTestScheduler nær VirtualTimeScheduler {
    spyFn: (actionName: string, delay: number, error?: any) => ógilt;
    framkvæmdaaðili () {
        frábær (VirtualAction, defaultMaxFrame);
    }
    onAction (spyFn: (actionName: string, delay: number, error?: any) => ógilt) {
        this.spyFn = spyFn;
    }
    skola () {
        const {aðgerðir, maxFrames} = þetta;
        láta villu: hvaða, aðgerð: AsyncAction ;
        meðan ((action = actions.shift ()) && (this.frame = action.delay) <= maxFrames) {
            láttu stateName = this.detectStateName (aðgerð);
            láta töf = aðgerð. seinka;
            ef (error = action.execute (action.state, action.delay)) {
                ef (this.spyFn) {
                    this.spyFn (ástandNafn, töf, villa);
                }
                brjóta;
            } Annar {
                ef (this.spyFn) {
                    this.spyFn (ríkiName, töf);
                }
            }
        }
        ef (villa) {
            meðan (action = actions.shift ()) {
                aðgerð.afskráðu þig ();
            }
            kastvilla;
        }
    }
    persónulegur detectStateName (aðgerð: AsyncAction ): strengur {
        const c = Object.getPrototypeOf (action.state). framkvæmdaaðili;
        const argsPos = c.toString (). indexOf ('(');
        ef (argsPos! == -1) {
            skila c.toString (). substring (9, argsPos);
        }
        skila núlli;
    }
}

Að lokum skulum við skoða notkunina. Dæmið er sama einingapróf og áður var notað (það („hreinsar fyrri niðurstöðu“) með þeim smá mun sem við ætlum að nota prufuáætlun í stað fakeAsync / tick.

láta testScheduler;
beforeEach (() => {
    testScheduler = nýr SpyingTestScheduler ();
    testScheduler.maxFrames = 1000000;
    monkeypatchScheduler (testScheduler);
    fastur búnaður.detectChanges ();
});
beforeEach (() => {
    spyOn (apiService, 'fyrirspurn'). og.callFake (() => {
        skila Observable.of (queryResult). Delay (REQUEST_DELAY);
    });
});
það ('hreinsar fyrri niðurstöðu', (gert: Virkni) => {
    comp.options = ['ekki tómt'];
    testScheduler.onAction ((aðgerðanafn: strengur, tafir: fjöldi, skjátlast ?: allir) => {
        ef (actionName === 'DebounceTimeSubscribber' && töf === DEBOUNCING_VALUE) {
            búast við (comp.options.length) .toBe (0, `var [$ {comp.options.join (',')}]`);
            gert ();
        }
    });
    SpecUtils.focusAndInput ('Londo', fastur búnaður, 'inntak');
    fastur búnaður.detectChanges ();
    testScheduler.flush ();
});

Prófa tímasettinn er búinn til og samsafnaður (!) Í fyrsta áður. Í annarri á hverju ári njósnum við um apiService.query til að geta þjónað niðurstöðu fyrirspurninniResult eftir REQUEST_DELAY = 5000 millisekúndur.

Við skulum fara í gegnum það línu fyrir línu:

  1. Fyrst af öllu, hafðu í huga að við lýsum yfir aðgerð sem við þurfum í tengslum við svarhringingu prufuáætlunarinnar við Aðgerð. Þetta þýðir að við verðum að segja Jasmine að prófið sé gert á eigin spýtur.
það ('hreinsar fyrri niðurstöðu', (gert: Virkni) => {

2. Aftur þykjum við nokkra valkosti sem eru til staðar í íhlutanum.

comp.options = ['ekki tómt'];

3. Þetta þarfnast skýringa vegna þess að það virðist vera svolítið klaufalegt við fyrstu sýn. Við viljum bíða aðgerðar sem kallast „DebounceTimeSubscribber“ með seinkun DEBOUNCING_VALUE = 300 millisekúndur. Þegar þetta gerist viljum við athuga hvort option.length er 0. Þá er prófinu lokið og við köllum lokið ().

testScheduler.onAction ((aðgerðanafn: strengur, tafir: fjöldi, skjátlast ?: allir) => {
    ef (actionName === 'DebounceTimeSubscribber' && töf === DEBOUNCING_VALUE) {
      búast við (comp.options.length) .toBe (0, `var [$ {comp.options.join (',')}]`);
      gert ();
    }
});

Þú sérð að notkun prófa tímaáætlana krefst nokkurrar sérstakrar þekkingar um innbyggingu Rxjs. Auðvitað fer það eftir því hvaða tímasetningar prófar þú notar en jafnvel þó þú útfærir öflugan tímaáætlun á eigin spýtur þarftu að skilja tímaáætlun og afhjúpa nokkur tímamörk fyrir sveigjanleika (sem, aftur, gæti ekki verið sjálfgefandi).

4. Aftur færir notandinn gildi „Londo“.

SpecUtils.focusAndInput ('Londo', fastur búnaður, 'inntak');

5. Aftur, uppgötvaðu breytingar og endurgerðu sniðmátið.

fastur búnaður.detectChanges ();

6. Að lokum framkvæmum við allar aðgerðir sem eru settar í biðröð tímasettar.

testScheduler.flush ();

Yfirlit

Eigin prufutæki Angular eru æskilegri en sjálfbúin ... svo lengi sem þau vinna. Í sumum tilvikum virkar fakeAsync / tick par ekki en það er engin ástæða til að örvænta og sleppa einingaprófunum. Í þessum tilvikum er sjálfvirkt samstillingarverkfæri (hér einnig þekkt sem AsyncZoneTimeInSyncKeeper) eða sérsniðin próf tímasett (hér einnig þekkt sem SpyingTestScheduler) leiðin.

Upprunakóði