Kampinis: Async duomenų tikrinimas „fakedAsync“ zonoje VS. Pasirinktinių planavimo priemonių teikimas

Man ne kartą buvo užduoti klausimai apie „padirbtą zoną“ ir kaip ja naudotis. Štai kodėl nusprendžiau parašyti šį straipsnį, kad pasidalyčiau savo pastebėjimais apie smulkius „fakeAsync“ testus.

Zona yra esminė kampinės ekosistemos dalis. Galima buvo skaityti, kad pati zona yra tik tam tikras „vykdymo kontekstas“. Tiesą sakant, kampinis beždžionėlis pateikia tokias globalias funkcijas kaip setTimeout arba setInterval, kad būtų sulaikytos funkcijos, vykdomos po tam tikro vėlavimo (setTimeout) arba periodiškai (setInterval).

Svarbu paminėti, kad šiame straipsnyje nenurodoma, kaip elgtis su „setTimeout“ įsilaužimais. Kadangi „Angular“ labai naudojasi „RxJ“, priklausančiomis nuo natūralių laiko nustatymo funkcijų (galite būti nustebinti, bet tai tiesa), ji naudoja zoną kaip sudėtingą, bet galingą įrankį, skirtą įrašyti visus asinchroninius veiksmus, kurie gali paveikti programos būseną. Kampinis juos įsiterpia norėdamas sužinoti, ar eilėje dar yra kokių nors darbų. Tai nusausina eilę, priklausomai nuo laiko. Greičiausiai nusausintos užduotys keičia komponentų kintamųjų reikšmes. Dėl to šablonas bus pakartotinai pateiktas.

Dabar visi async dalykai nėra tai, dėl ko turime jaudintis. Malonu suvokti, kas vyksta po gaubtu, nes tai padeda surašyti efektyvius vienetų testus. Be to, bandymų pagrindu sukurta plėtra daro didžiulį poveikį išeities kodams („TDD ištakos buvo noras gauti stiprų automatinį regresijos testą, palaikantį evoliucinį dizainą. Praktikos metu specialistai sužinojo, kad testų rašymas pirmiausia reikšmingai pagerino projektavimo procesą. „Martinas Fowleris, https://martinfowler.com/articles/mocksArentStubs.html, 2017 09 09).

Dėl visų šių pastangų galime pakeisti laiką, nes reikia patikrinti būklę tam tikru laiko momentu.

fakeAsync / pažymėkite kontūrą

Kampiniuose dokumentuose teigiama, kad fakeAsync (https://angular.io/guide/testing#fake-async) suteikia daugiau linijinio kodavimo patirties, nes atsikrato pažadų, tokių kaip .whenStable (), tada (…).

Kodas fakeAsync bloko viduje atrodo taip:

erkė (100); // laukite, kol bus atlikta pirmoji užduotis
fixture.detectChanges (); // atnaujinti vaizdą su citata
erkė (); // laukite, kol bus atlikta antra užduotis
fixture.detectChanges (); // atnaujinti vaizdą su citata

Šie fragmentai pateikia tam tikrą įžvalgą apie tai, kaip veikia fakeAsync.

„setTimeout“ / „setInterval“ yra naudojami čia, nes jie aiškiai rodo, kada funkcijos vykdomos fakeAsync zonoje. Galite tikėtis, kad ši „it“ funkcija turi žinoti, kada atliekamas testas („Jasmine“ sudėliota pagal pateiktą argumentą: „Funkcija“), tačiau šį kartą mes pasikliaujame „fakeAsync“ kompanionu, o ne naudojamės bet kokiu atgalinio ryšio būdu:

it ('nusausina zonos užduotį pagal užduotį', fakeAsync (() => {
        setTimeout (() => {
            tegul i = 0;
            const rankena = setInterval (() => {
                if (i ++ === 5) {
                    clearInterval (rankena);
                }
            }, 1000);
        }, 10000);
}));

Jis garsiai skundžiasi, nes eilėje dar yra keletas „laikmačių“ (= setTimeouts):

Klaida: 1 laikmatis vis dar yra eilėje.

Akivaizdu, kad turime pakeisti laiką, kad funkcija, kuriai skirtas laikas, būtų atlikta. Parametrizuotą „varnelę“ pridedame per 10 sekundžių:

erkė (10000);

Hugh? Klaida tampa dar painesnė. Dabar testas žlunga dėl paslėptų „periodinių laikmačių“ (= setIntervals):

Klaida: 1 periodinis laikmatis (-iai) vis dar yra eilėje.

Kadangi mes užfiksavome funkciją, kuri turi būti vykdoma kiekvieną sekundę, mes taip pat turime pakeisti laiką, naudodami vėl varnelę. Funkcija nustoja veikti po 5 sekundžių. Štai kodėl mums reikia pridėti dar 5 sekundes:

erkė (15000);

Dabar testas praėjo. Verta pasakyti, kad zona atpažįsta lygiagrečiai vykdomas užduotis. Tiesiog pratęskite funkciją, kurios laikas baigėsi, kitu „setInterval“ skambučiu.

it ('nusausina zonos užduotį pagal užduotį', fakeAsync (() => {
    setTimeout (() => {
        tegul i = 0;
        const rankena = setInterval (() => {
            if (++ i === 5) {
                clearInterval (rankena);
            }
        }, 1000);
        tegul j = 0;
        const handle2 = setInterval (() => {
            jei (++ j === 3) {
                clearInterval (rankena2);
            }
        }, 1000);
    }, 10000);
    erkė (15000);
}));

Testas vis dar išlaikytas, nes abu tie „setIntervals“ buvo pradėti tą pačią akimirką. Abu jie atliekami praėjus 15 sekundžių:

fakeAsync / pažymėti veiksme

Dabar mes žinome, kaip veikia fakeAsync / erkės. Leiskite tai naudoti prasmingiems dalykams.

Sukurkime į pasiūlymą panašų lauką, kuris atitiktų šiuos reikalavimus:

  • jis patraukia rezultatą iš kai kurios API (paslaugos)
  • jis atmeta vartotojo įvestį, kad lauktų galutinio paieškos termino (sumažėja užklausų skaičius); DEBOUNCING_VALUE = 300
  • jis rodo rezultatą vartotojo sąsajoje ir išsiunčia atitinkamą pranešimą
  • vieneto testas atsižvelgia į asinchroninį kodo pobūdį ir patikrina, ar tinkamas į lauką panašaus lauko elgesys atsižvelgiant į praleistą laiką

Pabaigoje pateikiame šį testavimo scenarijų:

aprašyti („ieškant“, () => {
    it ('išvalo ankstesnį rezultatą', fakeAsync (() => {
    }));
    it ('skleidžia pradžios signalą', fakeAsync (() => {
    }));
    it („atmeta galimus API įvykius iki 1 užklausos per DEBOUNCING_VALUE milisekundę“, fakeAsync (() => {
    }));
});
aprašyti ('ant sėkmės', () => {
    tai („vadina„ Google “API“, fakeAsync (() => {
    }));
    it ('skleidžia sėkmės signalą su atitikmenų skaičiumi', fakeAsync (() => {
    }));
    it ('rodo pavadinimus pasiūlymo laukelyje', fakeAsync (() => {
    }));
});
aprašyti ('dėl klaidos', () => {
    it ('skleidžia klaidos signalą', fakeAsync (() => {
    }));
});

„Paieškos“ metu nelaukiame paieškos rezultato. Kai vartotojas pateikia įvestį (pvz., „Lon“), reikia išvalyti ankstesnes parinktis. Mes tikimės, kad variantai bus tušti. Be to, vartotojo įvestis turi būti sumažinta, tarkime 300 milisekundžių verte. Kalbant apie zoną, 300 milijonų mikrotaskų stumiama į eilę.

Atminkite, kad praleidžiu keletą detalių dėl trumpumo:

  • bandymo sąranka yra beveik tokia pati, kaip matoma „Kampiniuose dokumentuose“
  • „apiService“ egzempliorius suleidžiamas per „fixture.debugElement.injector“ (…)
  • „SpecUtils“ suaktyvina su vartotoju susijusius įvykius, tokius kaip įvestis ir fokusavimas
beforeEach (() => {
    „spyOn“ („apiService“, „užklausa“) ir.returnValue (Observable.of (queryResult));
});
tinka („išvalo ankstesnį rezultatą“, fakeAsync (() => {
    comp.options = ['netuščia'];
    „SpecUtils.focusAndInput“ ('Lon', armatūra, 'input');
    varnelė (DEBOUNCING_VALUE);
    fixture.detectChanges ();
    tikėtis (comp.options.length) .toBe (0, `buvo [$ {comp.options.join (',')}] '));
}));

Komponento kodas, bandantis įvykdyti testą:

ngOnInit () {
    this.control.valueChanges.debounceTime (300) .subscribe (value => {
        this.options = [];
        this.suggest (vertė);
    });
}
pasiūlyti (q: eilutė) {
    this.googleBooksAPI.query (q) .prenumeruoti (rezultatas => {
// ...
    }, () => {
// ...
    });
}

Pereikime kodą žingsnis po žingsnio:

Mes šnipinėjame „apiService“ užklausos metodą, kurį mes vadinsime komponentu. Kintamame „queryResult“ yra keletas apgaulingų duomenų, tokių kaip „Hamlet“, „Macbeth“ ir „King Lear“. Iš pradžių mes tikimės, kad parinktys bus tuščios, tačiau, kaip jau galėjote pastebėti, visa fakeAsync eilė nuteka su varnele (DEBOUNCING_VALUE), todėl komponentas pateikia ir galutinį Shakespeare'o raštų rezultatą:

Tikimasi, kad 3 bus 0, „buvo [Hamletas, Makbetas, karalius Learas]“.

Mums reikia atidėti užklausą dėl paslaugos užklausos, kad būtų galima emuliuoti asinchroninį laiką, kurį sunaudoja API skambutis. Pridėkime 5 sekundžių vėlavimą (REQUEST_DELAY = 5000) ir pažymėkime (5000).

beforeEach (() => {
    „spyOn“ („apiService“, „užklausa“) ir.returnValue (Observable.of (queryResult) .delay (1000));
});

tinka („išvalo ankstesnį rezultatą“, fakeAsync (() => {
    comp.options = ['netuščia'];
    „SpecUtils.focusAndInput“ ('Lon', armatūra, 'input');
    varnelė (DEBOUNCING_VALUE);
    fixture.detectChanges ();
    tikėtis (comp.options.length) .toBe (0, `buvo [$ {comp.options.join (',')}] '));
    pažymėti (REQUEST_DELAY);
}));

Mano nuomone, šis pavyzdys turėtų veikti, bet „Zone.js“ tvirtina, kad eilėje dar yra šiek tiek darbo:

Klaida: 1 periodinis laikmatis (-iai) vis dar yra eilėje.

Šiuo metu turime įsigilinti, kad pamatytume tas funkcijas, kurios, kaip įtariame, įstrigo zonoje. Kelių išeities taškų nustatymas yra kelias:

derinimo fakeAsync zona

Tada išleiskite tai komandinėje eilutėje

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

arba išnagrinėkite zonos turinį taip:

hmmm, AsyncScheduler plovimo metodas vis dar yra eilėje ... kodėl?

Pabrėžtos funkcijos pavadinimas yra „AsyncScheduler“ įpūtimo metodas.

visuomenės pylimas (veiksmas: „AsyncAction“ <)>: negaliojantis {
  const {action} = tai;
  if (this.active) {
    veiksmai.push (veiksmas);
    grįžti;
  }
  tegul error: bet koks;
  this.active = true;
  daryti {
    if (klaida = action.execute (action.state, action.delay)) {
      pertrauka;
    }
  } while (veiksmas = action.shift ()); // išmeskite planuotojo eilę
  this.active = false;
  if (klaida) {
    o (veiksmas = action.shift ()) {
      veiksmas.prenumeruoti ();
    }
    mesti klaidą;
  }
}

Dabar gali kilti klausimas, kas blogo pačiame šaltinio kode ar zonoje.

Problema ta, kad zona ir mūsų erkės nėra suderintos.

Pati zona turi dabartinį laiką (2017 m.), Tuo tarpu erkė nori įvykdyti veiksmą, numatytą 1970 m. Sausio 1 d. + 300 mln.

Async planavimo priemonės vertė patvirtina, kad:

importuoti {async as Ascnccheduler} iš 'rxjs / planuoklis / async';
// padėkite tai kažkur „it“ viduje
console.info (AsyncScheduler.now ());
// → 1503235213879

AsyncZoneTimeInSyncKeeper į gelbėjimą

Vienas iš galimų šios problemos taisymo būdų yra „sinchronizavimo palaikymo“ programa:

eksporto klasė „AsyncZoneTimeInSyncKeeper“ {
    laikas = 0;
    konstruktorius () {
        „spyOn“ („AsyncScheduler“, „dabar“) ir.callFake (() => {
            / * šlifas: išjungti kitą eilutę * /
            console.info ('laikas', this.time);
            grąžinti šį laiką;
        });
    }
    pažymėti (laikas ?: skaičius) {
        if (typeof time! == 'neapibrėžta') {
            this.time + = laikas;
            varnele (this.time);
        } Kitas {
            erkė ();
        }
    }
}

Jis seka dabartinį laiką, kurį grąžina dabar (), kai yra iškviečiamas asinchroninio planavimo įrenginys. Tai veikia, nes varnelės () funkcija naudoja tą patį dabartinį laiką. Tiek planavimo priemonė, tiek zona turi tą patį laiką.

Aš rekomenduoju akimirksniu pritaikyti „timeInSyncKeeper“ ankstesniame etape:

aprašyti („ieškant“, () => {
    let timeInSyncKeeper;
    beforeEach (() => {
        timeInSyncKeeper = naujas AsyncZoneTimeInSyncKeeper ();
    });
});

Pažvelkime į laiko sinchronizavimo laikiklio naudojimą. Atminkite, kad turime išspręsti šią laiko problemą, nes teksto laukas nurašomas ir užklausa užtrunka.

aprašyti („ieškant“, () => {
    let timeInSyncKeeper;
    beforeEach (() => {
        timeInSyncKeeper = naujas AsyncZoneTimeInSyncKeeper ();
        „spyOn“ („apiService“, „užklausa“) ir.returnValue (Observable.of (queryResult) .delay (REQUEST_DELAY));
    });
    it ('išvalo ankstesnį rezultatą', fakeAsync (() => {
        comp.options = ['netuščia'];
        „SpecUtils.focusAndInput“ ('Lon', armatūra, 'input');
        „timeInSyncKeeper.tick“ (DEBOUNCING_VALUE);
        fixture.detectChanges ();
        tikėtis (comp.options.length) .toBe (0, `buvo [$ {comp.options.join (',')}] '));
        „timeInSyncKeeper.tick“ (REQUEST_DELAY);
    }));
    // ...
});

Pereikime šį pavyzdį eilutės po eilutės:

  1. pagreitinkite sinchronizavimo laikiklio egzempliorių
timeInSyncKeeper = naujas AsyncZoneTimeInSyncKeeper ();

2. Leiskite atsakyti į apiService.query metodą rezultatu „queryResult“ praėjus REQUEST_DELAY. Tarkime, užklausos metodas yra lėtas ir reaguoja po REQUEST_DELAY = 5000 milisekundžių.

„spyOn“ („apiService“, „užklausa“) ir.returnValue (Observable.of (queryResult) .delay (REQUEST_DELAY));

3. Apsimesti, kad pasiūlymo lauke yra parinktis „ne tuščia“

comp.options = ['netuščia'];

4. Eikite į lauką „input“, esančią pagrindiniame armatūros elemente, ir įveskite reikšmę „Lon“. Tai imituoja vartotojo sąveiką su įvesties lauku.

„SpecUtils.focusAndInput“ ('Lon', armatūra, 'input');

5. leiskite pravažiuoti DEBOUNCING_VALUE laiko tarpą netikroje asinchroninėje zonoje (DEBOUNCING_VALUE = 300 milisekundžių).

„timeInSyncKeeper.tick“ (DEBOUNCING_VALUE);

6. Aptikite pakeitimus ir iš naujo pateikite HTML šabloną.

fixture.detectChanges ();

7. Parinkčių masyvas tuščias!

tikėtis (comp.options.length) .toBe (0, `buvo [$ {comp.options.join (',')}] '));

Tai reiškia, kad komponentų naudojami stebimi vertės pokyčiai buvo valdomi tinkamu laiku. Atminkite, kad vykdoma funkcija „debounceTime-d“

reikšmė => {
    this.options = [];
    this.onEvent.emit ({signalas: SuggestSignal.start});
    this.suggest (vertė);
}

įtraukė dar vieną užduotį į eilę, paskambindamas metodu:

pasiūlyti (q: eilutė) {
    if (! q) {
        grįžti;
    }
    this.googleBooksAPI.query (q) .prenumeruoti (rezultatas => {
        if (rezultatas) {
            this.options = rezultatas.items.map (item => item.volumeInfo);
            this.onEvent.emit ({signalas: SuggestSignal.success, totalItems: result.totalItems});
        } Kitas {
            this.onEvent.emit ({signalas: SuggestSignal.success, totalItems: 0});
        }
    }, () => {
        this.onEvent.emit ({signalas: SuggestSignal.error});
    });
}

Tiesiog prisiminkite „Google“ knygų API užklausos metodo šnipą, kuris reaguoja po 5 sekundžių.

8. Galiausiai, norėdami praplauti zonos eilę, turime dar kartą pažymėti, kad REQUEST_DELAY = 5000 milisekundžių. Stebimam, kurį mes užsisakome pagal siūlomą metodą, reikia REQUEST_DELAY = 5000, kad jis būtų baigtas.

„timeInSyncKeeper.tick“ (REQUEST_DELAY);

fakeAsync…? Kodėl? Yra planuotojai!

„ReactiveX“ ekspertai gali teigti, kad mes galime naudoti testų planavimo priemones, kad stebimi elementai būtų išbandomi. Tai galima pritaikyti kampiniams tikslams, tačiau ji turi keletą trūkumų:

  • tai reikalauja išmanyti vidinę stebimųjų elementų, operatorių struktūrą ir…
  • Ką daryti, jei jūsų programoje yra keletas bjaurių „setTimeout“ sprendimo būdų? Su jais nesusitvarko planuotojai.
  • pats svarbiausias: esu tikras, kad nenorite naudoti planuoklių visoje savo programoje. Nenorite maišyti gamybos kodo su vieneto bandymais. Nenorite daryti kažko panašaus:
const testScheduler;
if (aplinka.testas) {
    testScheduler = naujas „YourTestScheduler“ ();
}
tegul stebimas;
if (testScheduler) {
    stebimas = stebimas. vertės (vertės). vėlavimas (1000, testScheduler)
} Kitas {
    stebimas = stebimas. vertės (vertės) .vėlavimas (1000);
}

Tai nėra perspektyvus sprendimas. Mano nuomone, vienintelis įmanomas sprendimas yra „įšvirkšti“ testų planuoklę, pateikiant „tarpinius“ duomenis tikriems Rxjs metodams. Kitas dalykas, į kurį reikia atsižvelgti, yra tas, kad svarbesni metodai gali turėti neigiamos įtakos likusiems vieneto bandymams. Štai kodėl mes naudosime Jasmine šnipus. Po kiekvieno šnipo pašalinami šnipai.

Funkcija „monkeypatchScheduler“ apvynioja originalų „Rxjs“ įgyvendinimą naudojant šnipą. Šnipas atsižvelgia į metodo argumentus ir, jei reikia, prideda „testScheduler“.

importuoti {IScheduler} iš 'rxjs / Scheduler';
importuoti {Observable} iš 'rxjs / Observable';
deklaruoja var spyOn: Funkcija;
eksporto funkcija monkeypatchScheduler (planavimo priemonė: IScheduler) {
    let observableMethods = ['concat', 'atidėti', 'tuščia', 'forkJoin', 'if', 'intervalas', 'sujungti', 'iš', 'diapazonas', 'mesti',
        'zip'];
    tegul operatorMethods = ['buferis', 'concat', 'atidėti', 'aiškus', 'daryti', 'kiekvienas', 'paskutinis', 'sujungti', 'max', 'imtis',
        'timeInterval', 'lift', 'debounceTime'];
    tegul injectFn = function (bazė: bet kuri, metodai: eilutė []) {
        method.forEach (metodas => {
            const orig = bazė [metodas];
            if (typeof orig === 'function') {
                spyOn (bazė, metodas) .and.callFake (function () {
                    tegul args = Array.prototype.slice.call (argumentai);
                    if (args [args.length - 1] && typeof args [args.length - 1] .now === 'function') {
                        args [args.length - 1] = planuoklė;
                    } Kitas {
                        args.push (planuotojas);
                    }
                    grįžti orig.apply (tai, args);
                });
            }
        });
    };
    injectFn (Stebimi, stebimi metodai);
    injectFn (stebimas.prototipas, operatorMetodai);
}

Nuo šiol „testScheduler“ vykdys visą darbą „Rxjs“ viduje. Jis nenaudoja „setTimeout“ / „setInterval“ ar bet kokio pobūdžio asinchroninio turinio. „FakeAsync“ nebereikia.

Dabar mums reikia bandymo planavimo priemonės, kurią norime perduoti monkeypatchScheduler.

Jis elgiasi labai panašiai kaip numatytasis „TestScheduler“, tačiau jis siūlo „atgalinio ryšio“ metodą „OnAction“. Tokiu būdu mes žinome, kuris veiksmas buvo atliktas po kurio laiko.

eksporto klasė „SpyingTestScheduler“ pratęsia „VirtualTimeScheduler“ {
    spyFn: (actionName: eilutė, delsimas: skaičius, klaida ?: bet kokia) => negalioja;
    konstruktorius () {
        super („VirtualAction“, „defaultMaxFrame“);
    }
    „onAction“ (spyFn: (actionName: eilutė, delsimas: skaičius, klaida ?: bet kokia) => negalioja) {
        this.spyFn = spyFn;
    }
    praplaukite () {
        const {action, maxFrames} = tai;
        tegul error: bet koks, veiksmas: AsyncAction ;
        o ((veiksmas = action.shift ()) && (this.frame = action.delay) <= maxFrames) {
            tegul stateName = this.detectStateName (veiksmas);
            tegul atidėti = action.delay;
            if (klaida = action.execute (action.state, action.delay)) {
                if (this.spyFn) {
                    this.spyFn (stateName, atidėjimas, klaida);
                }
                pertrauka;
            } Kitas {
                if (this.spyFn) {
                    this.spyFn (stateName, atidėjimas);
                }
            }
        }
        if (klaida) {
            o (veiksmas = action.shift ()) {
                veiksmas.prenumeruoti ();
            }
            mesti klaidą;
        }
    }
    „private deteStateName“ (veiksmas: „AsyncAction“ <)>: eilutė {
        const c = Object.getPrototypeOf (action.state) .konstruktorius;
        const argsPos = c.toString (). indexOf ('(');
        if (argsPos! == -1) {
            grįžti c.toString (). substring (9, argsPos);
        }
        grąžinti nulinį;
    }
}

Galiausiai, pažiūrėkime apie naudojimą. Pavyzdys yra tas pats vieneto testas, kuris buvo naudojamas anksčiau (jis („išvalo ankstesnį rezultatą“) su nedideliu skirtumu, kad vietoj fakeAsync / varnelės naudosime bandymo planuoklę).

let testScheduler;
beforeEach (() => {
    testScheduler = naujas „SpyingTestScheduler“ ();
    „testScheduler.maxFrames“ = 1000000;
    „monkeypatchScheduler“ („testScheduler“);
    fixture.detectChanges ();
});
beforeEach (() => {
    „spyOn“ („apiService“, „užklausa“) ir.callFake (() => {
        grąžinti „Observable.of“ (queryResult) .delay (REQUEST_DELAY);
    });
});
it ('išvalo ankstesnį rezultatą', (atlikta: Funkcija) => {
    comp.options = ['netuščia'];
    testScheduler.onAction ((„actionName“: eilutė, delsimas: skaičius, klaida ?: bet kokia) => {
        if (actionName === 'DebounceTimeSubscriber' && atidėlioti === DEBOUNCING_VALUE) {
            tikėtis (comp.options.length) .toBe (0, `buvo [$ {comp.options.join (',')}] '));
            padaryta();
        }
    });
    „SpecUtils.focusAndInput“ ('Londo', armatūra, 'įvestis');
    fixture.detectChanges ();
    testScheduler.flush ();
});

Testo planavimo priemonė yra sukurta ir monkeypatched (!) Pirmame prieš kiekvieną. Antrame prieš kiekvieną „mes“ šnipinėjame „apiService.query“, kad būtų pateiktas užklausos rezultatas užklausai REQUEST_DELAY = 5000 milisekundžių.

Dabar eikime per jį eilutė po eilutės:

  1. Pirmiausia atkreipkite dėmesį, kad mes deklaruojame atliktą funkciją, kurios mums reikia kartu su bandymo planuokliu, „callback onAction“. Tai reiškia, kad turime pasakyti Jasmine, kad testas atliekamas mūsų pačių.
it ('išvalo ankstesnį rezultatą', (atlikta: Funkcija) => {

2. Vėlgi, mes apsimetame kai kurias komponento galimybes.

comp.options = ['netuščia'];

3. Tam reikia šiek tiek paaiškinti, nes iš pirmo žvilgsnio jis atrodo šiek tiek gremėzdiškas. Norime laukti veiksmo, pavadinto „DebounceTimeSubscriber“, su DEBOUNCING_VALUE = 300 milisekundžių vėlavimu. Kai tai atsitiks, norime patikrinti, ar options.length yra 0. Tada testas yra baigtas ir mes vadiname baigtu ().

testScheduler.onAction ((„actionName“: eilutė, delsimas: skaičius, klaida ?: bet kokia) => {
    if (actionName === 'DebounceTimeSubscriber' && atidėlioti === DEBOUNCING_VALUE) {
      tikėtis (comp.options.length) .toBe (0, `buvo [$ {comp.options.join (',')}] '));
      padaryta();
    }
});

Matote, kad norint naudoti testų planuoklius, reikia specialių žinių apie „Rxjs“ diegimo vidinius dalykus. Žinoma, tai priklauso nuo to, kokį testų planuoklį naudojate, tačiau net ir savarankiškai įgyvendindami galingą planuoklį, turėsite suprasti suplanuotuosius grafikus ir atskleisti keletą lankstumo vykdymo laiko verčių (kurios, vėlgi, gali būti savaime suprantamos).

4. Vėlgi, vartotojas įveda vertę „Londo“.

„SpecUtils.focusAndInput“ ('Londo', armatūra, 'įvestis');

5. Vėlgi, nustatykite pakeitimus ir iš naujo pateikite šabloną.

fixture.detectChanges ();

6. Galiausiai vykdome visus veiksmus, įdėtus į planuotojo eilę.

testScheduler.flush ();

Santrauka

Pačios „Anglic“ bandymo priemonės yra geresnės nei pačios pagamintos ... tol, kol jos veikia. Kai kuriais atvejais fakeAsync / erkių pora neveikia, tačiau nėra jokios priežasties desperatiškai ir praleisti vienetų testus. Tokiais atvejais yra automatinio sinchronizavimo naudingumas (čia taip pat žinomas kaip AsyncZoneTimeInSyncKeeper) arba pasirinktinis bandymų planuoklis (čia taip pat žinomas kaip SpyingTestScheduler).

Pirminis kodas