Burchaklar: Async VS faks zonasida async materiallarini sinash. buyurtmachilarni ta'minlash

Menga "soxta zona" va undan qanday foydalanish haqida bir necha bor savol berishdi. Shuning uchun men ushbu maqolani nozik "fakeAsync" testlari haqida gap ketganda o'z fikrlarim bilan o'rtoqlashish uchun yozishga qaror qildim.

Zona Burchak ekotizimining hal qiluvchi qismidir. Zonaning o'zi "ijro etilish konteksti" ning o'ziga xos turi ekanligini o'qigan bo'lishi mumkin. Aslida, burchakli maymun ba'zi kechikish (setTimeout) yoki vaqti-vaqti bilan (setInterval) keyin bajarilayotgan funktsiyalarni ushlab turish uchun setTimeout yoki setInterval kabi global funktsiyalarni bajaradi.

Shuni ta'kidlash kerakki, ushbu maqola setTimeout hacklariga qanday qarshi kurashishni ko'rsatmaydi. Angular mahalliy vaqt funktsiyalariga tayanadigan RxJ-larni juda qattiq ishlatganligi sababli (siz hayron bo'lishingiz mumkin, ammo bu haqiqat), u dasturni holatiga ta'sir qilishi mumkin bo'lgan barcha asenkron harakatlarni qayd etish uchun murakkab, ammo kuchli vosita sifatida zonadan foydalanadi. Burchaklar hali biron bir ish bor yoki yo'qligini bilish uchun ularni ushlaydi. Vaqtga qarab navbatni quritadi. Ehtimol, quritilgan vazifalar komponent o'zgaruvchilarining qiymatlarini o'zgartiradi. Natijada shablon qayta ko'rsatiladi.

Endi, barcha async narsalar biz tashvishlanadigan narsa emas. Kaput ostida nimalar sodir bo'layotganini tushunish juda yoqimli, chunki bu jihozlarni samarali sinovlarini yozishga yordam beradi. Bundan tashqari, test asosida ishlab chiqilgan dastur manba kodiga juda katta ta'sir ko'rsatadi ("TDD ning kelib chiqishi evolyutsion dizaynni qo'llab-quvvatlaydigan kuchli avtomatik regressiya sinovlarini olish istagi edi. Amaliyotchilar yozuv testlari dastlab dizayn jarayonini sezilarli darajada yaxshilaganligini aniqladilar). “Martin Fouler, https://martinfowler.com/articles/mocksArentStubs.html, 09/2017).

Ushbu sa'y-harakatlar natijasida biz vaqtni o'zgartirishimiz mumkin, chunki biz aniq bir vaqtda davlatni sinab ko'rishimiz kerak.

fakeAsync / belgilang

Burchak hujjatlarida aytilishicha, fakeAsync (https://angular.io/guide/testing#fake-async) kodlashning yanada chiziqli tajribasini beradi, chunki u .whenStable (). Keyin (…) kabi va'dalardan xalos bo'ladi.

FakeAsync blokidagi kod quyidagicha ko'rinadi:

shomil (100); // birinchi topshiriq bajarilishini kuting
fixture.detectChanges (); // sitata bilan yangilash ko'rinishi
belgi (); // ikkinchi topshiriq bajarilishini kuting
fixture.detectChanges (); // sitata bilan yangilash ko'rinishi

Quyidagi parchalar fakeAsync qanday ishlashi haqida ba'zi tushunchalar beradi.

setTimeout / setInterval bu erda ishlatiladi, chunki ular fakeAsync zonasida funktsiyalar qachon bajarilishini aniq ko'rsatib beradi. Siz "bu" funktsiyani sinov qachon amalga oshirilishini bilishingiz kerak deb taxmin qilishingiz mumkin (Yasemin tomonidan argument bo'yicha tuzilgan: Funktsiya), ammo bu safar biz har qanday qayta qo'ng'iroq qilishdan ko'ra, fakeAsync-ga murojaat qilamiz:

it ('vazifani zona bo'yicha quritadi', fakeAsync (() => {
        setTimeout (() => {
            ruxsat bering i = 0;
            const dastasi = setInterval (() => {
                if (i ++ === 5) {
                    clearInterval (dastani);
                }
            }, 1000);
        }, 10000);
}));

Bu baland ovoz bilan shikoyat qiladi, chunki navbatda "taymerlar" (= setTimeouts) mavjud:

Xato: 1 ta taymer (lar) navbatda.

Kutilgan vazifani bajarish uchun vaqtni o'zgartirishimiz kerakligi aniq. Biz parametrlangan "belgi" ni 10 soniya bilan qo'shamiz:

shomil (10000);

Hugh? Xato chalkashib ketmoqda. Endi sinov davri "davriy taymerlar" (= setIntervals) tufayli muvaffaqiyatsiz tugadi:

Xato: 1 ta davriy taymer (lar) navbatda.

Biz har bir sekundda bajarilishi kerak bo'lgan funktsiyani qabul qilganimiz sababli, biz yana bir marta Shomil yordamida vaqtni o'zgartirishimiz kerak. Funktsiya 5 soniyadan so'ng o'zini tugatadi. Shu sababli yana 5 soniya qo'shib qo'yishimiz kerak:

shomil (15000);

Endi sinovlar o'tmoqda. Aytish kerakki, zona parallel ravishda bajariladigan vazifalarni tan oladi. Kutilgan vazifani boshqa setInterval qo'ng'irog'i orqali kengaytiring.

it ('vazifani zona bo'yicha quritadi', fakeAsync (() => {
    setTimeout (() => {
        ruxsat bering i = 0;
        const dastasi = setInterval (() => {
            if (++ i === 5) {
                clearInterval (dastani);
            }
        }, 1000);
        qilaylik j = 0;
        const handle2 = setInterval (() => {
            if (++ j === 3) {
                clearInterval (dastak2);
            }
        }, 1000);
    }, 10000);
    shomil (15000);
}));

Sinov hali ham davom etmoqda, chunki ikkala setIntervals bir vaqtning o'zida boshlangan. Ikkalasi ham 15 soniya o'tgandan so'ng amalga oshiriladi:

fakeAsync / amalda belgilang

Endi biz fakeAsync / malumotlarning qanday ishlashini bilamiz. Undan ba'zi mazmunli narsalar uchun foydalanishga ruxsat bering.

Ushbu talablarga javob beradigan takliflar maydonini yarataylik.

  • natijani ba'zi API (xizmat) orqali oladi
  • qidiruvning yakuniy vaqtini kutish uchun foydalanuvchi kiritgan ma'lumotni siqib chiqaradi (so'rovlar sonini kamaytiradi); DEBOUNCING_VALUE = 300
  • natijani UI-da ko'rsatadi va tegishli xabarni chiqaradi
  • Birlik testi kodning asenkron xususiyatini hurmat qiladi va vaqtni taqqoslashda taklif qilingan maydonning to'g'ri ishlashini tekshiradi

Ushbu sinov stsenariylari bilan yakunlaymiz:

ta'riflang ('qidiruvda', () => {
    u ('oldingi natijani o'chiradi', fakeAsync (() => {
    }));
    u ('boshlang'ich signalini chiqaradi'), fakeAsync (() => {
    }));
    u ('API uchun mumkin bo'lgan xitlarni DEBOUNCING_VALUE millisekundlarda 1 ta so'rovga qaratmoqda', fakeAsync (() => {
    }));
});
ta'riflang ('muvaffaqiyat to'g'risida', () => {
    u ('google API-ni chaqiradi), fakeAsync (() => {
    }));
    u ('mos keladiganlar soni bilan muvaffaqiyat signalini chiqaradi'), fakeAsync (() => {
    }));
    u ('sarlavhalarni taklif qilingan maydonda ko'rsatadi', fakeAsync (() => {
    }));
});
ta'riflang ('xato haqida', () => {
    u ('xato signalini chiqaradi', fakeAsync (() => {
    }));
});

"Qidiruvda" biz qidiruv natijasini kutmaymiz. Agar foydalanuvchi kirishni ta'minlasa (masalan "Lon"), avvalgi parametrlarni tozalash kerak. Variantlar bo'sh bo'lishini kutamiz. Bundan tashqari, foydalanuvchining kiritgan ma'lumotlari baholanishi kerak, aytaylik 300 millisekundga teng. Zona jihatidan, 300 millimetr mikrotask navbatga quyiladi.

E'tibor bering, qisqartirish uchun ba'zi tafsilotlarni qoldirib ketdim:

  • sinov sozlamalari burchak hujjatlarida bo'lgani kabi bir xil
  • apiService misoli fixture.debugElement.injector orqali yuboriladi (…)
  • SpecUtils kirish va fokus kabi foydalanuvchi bilan bog'liq voqealarni qo'zg'atadi
oldinEach (() => {
    spyOn (apiService, 'query'). va.returnValue (Observable.of (queryResult));
});
fit ('oldingi natijani o'chiradi'), fakeAsync (() => {
    comp.options = ['bo'sh emas'];
    SpecUtils.focusAndInput ('Lon', armatura, 'kiritish');
    belgi (DEBOUNCING_VALUE);
    fixture.detectChanges ();
    kutish (comp.options.length) .toBe (0, `[$ {comp.options.join (',')}]`) edi;
}));

Sinovni qondirishga urinayotgan komponent kodi:

ngOnInit () {
    this.control.valueChanges.debounceTime (300). obunani bekor qilish (value => {
        this.options = [];
        this.suggest (value);
    });
}
tavsiya qilish (q: string) {
    this.googleBooksAPI.query (q). obunani bekor qilish (natija => {
// ...
    }, () => {
// ...
    });
}

Kodni bosqichma-bosqich ko'rib chiqaylik:

Biz komponentda qo'ng'iroq qilmoqchi bo'lgan apiService so'rov usuliga josuslik qilamiz. O'zgaruvchan queryResult "Gamlet", "Makbet" va "King Lir" kabi soxta ma'lumotlarni o'z ichiga oladi. Dastlab biz variantlar bo'sh bo'lishini kutamiz, ammo siz fakeAsync navbati bilan belgilang (DEBOUNCING_VALUE), shuning uchun komponent Shekspir yozuvlarining yakuniy natijasini o'z ichiga oladi:

Kutilgan 3 ning 0 bo'lishi '' [Gamlet, Makbet, King Lir] 'edi.

API qo'ng'irog'i orqali sarflanadigan vaqtning asenkron o'tishini taqlid qilish uchun biz xizmat so'rovining so'roviga kechikish kerak. 5 soniya kechikish (REQUEST_DELAY = 5000) va (5000) belgilang.

oldinEach (() => {
    spyOn (apiService, 'query') va.returnValue (Observable.of (queryResult) .delay (1000));
});

fit ('oldingi natijani o'chiradi'), fakeAsync (() => {
    comp.options = ['bo'sh emas'];
    SpecUtils.focusAndInput ('Lon', armatura, 'kiritish');
    belgi (DEBOUNCING_VALUE);
    fixture.detectChanges ();
    kutish (comp.options.length) .toBe (0, `[$ {comp.options.join (',')}]`) edi;
    belgi (REQUEST_DELAY);
}));

Menimcha, bu misol ishlashi kerak, ammo Zone.js navbatda hali ba'zi ishlar borligini ta'kidlaydi:

Xato: 1 ta davriy taymer (lar) navbatda.

Bu erda biz zonada qoqilib qolishimiz mumkin bo'lgan funktsiyalarni ko'rish uchun chuqurroq borishimiz kerak. Ba'zi bir nosozlik nuqtalarini belgilash - bu yo'l.

fakeAsync zonasini tuzatish

Keyin, buyruq satriga bu haqda yozing

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

yoki zonaning tarkibini quyidagicha tekshiring:

hmmm, AsyncSchedulerning tozalash usuli hanuz navbatda ... nega?

Belgilangan funktsiya nomi - AsyncScheduler's's flush usuli.

ommaviy tozalash (harakat: AsyncAction ): void {
  const {harakatlar} = bu;
  if (this.active) {
    harakatlar.push (harakat);
    qaytish;
  }
  let error: har qanday;
  this.active = rost;
  qilish {
    if (error = action.execute (action.state, action.delay)) {
      sindirish;
    }
  } while (action = action.shift ()); // rejalashtirgichni navbatini tugatish
  this.active = noto'g'ri;
  if (xato) {
    while (action = action.shift ()) {
      action.unsubscribe ();
    }
    otish xatosi;
  }
}

Endi, manba kodi yoki zonaning o'zi bilan nima noto'g'ri ekanligi haqida savol tug'ilishi mumkin.

Muammo shundaki, zona va bizning malhamlarimiz o'zaro bog'liq emas.

Zonaning o'zi joriy vaqtga (2017) ega, malumot 01.01.1970 + 300 millis + 5 soniyada rejalashtirilgan harakatlarni bajarishni istaydi.

Async rejalashtiruvchisining qiymati quyidagilarni tasdiqlaydi:

'rxjs / scheduler / async' dan {async AsyncScheduler} sifatida import qilish;
// bu joyga "u" ning ichiga joylashtiring
konsol.info (AsyncScheduler.now ());
// → 1503235213879

AsyncZoneTimeInSyncKeeper qutqaruvchiga

Sinovlarni sinxronlashtirish yordamchi dasturiga ega bo'lishi mumkin:

AsyncZoneTimeInSyncKeeper {eksport sinfi
    vaqt = 0;
    konstruktor () {
        spyOn (AsyncScheduler, 'hozir'). va .callFake (() => {
            / * tslint: o'chirish-keyingi qator * /
            konsol.info ('vaqt', this.time);
            return this.time;
        });
    }
    belgilang (vaqt?: raqam) {
        if (vaqtning turi! == 'aniqlanmagan') {
            this.time + = vaqt;
            belgi (this.time);
        } else {
            belgi ();
        }
    }
}

Async rejalashtiruvchisi chaqirilganda, hozir () tomonidan qaytariladigan joriy vaqtni hisobga oladi. Bu ishora qiladi, chunki Shomil () funktsiyasi joriy vaqtni ishlatadi. Ikkala, ham rejalashtiruvchi va mintaqa bir xil vaqtni baham ko'radi.

Men TimeInSyncKeeper dasturini avvalgi bosqichda o'rnatishni maslahat beraman:

ta'riflang ('qidiruvda', () => {
    vaqt berishInSyncKeeper;
    oldinEach (() => {
        timeInSyncKeeper = yangi AsyncZoneTimeInSyncKeeper ();
    });
});

Endi sinxronlash vaqtini qanday ishlatishni ko'rib chiqaylik. Shuni yodda tutingki, biz vaqtni aniqlash muammosini hal qilishimiz kerak, chunki matn maydoni o'chirildi va so'rov biroz vaqt talab etadi.

ta'riflang ('Qidiruvda', () => {
    vaqt berishInSyncKeeper;
    oldinEach (() => {
        timeInSyncKeeper = yangi AsyncZoneTimeInSyncKeeper ();
        spyOn (apiService, 'query'). va.returnValue (Observable.of (queryResult) .yoshlash (REQUEST_DELAY));
    });
    u ('oldingi natijani o'chiradi', fakeAsync (() => {
        comp.options = ['bo'sh emas'];
        SpecUtils.focusAndInput ('Lon', armatura, 'kiritish');
        timeInSyncKeeper.tick (DEBOUNCING_VALUE);
        fixture.detectChanges ();
        kutish (comp.options.length) .toBe (0, `[$ {comp.options.join (',')}]`) edi;
        timeInSyncKeeper.tick (REQUEST_DELAY);
    }));
    // ...
});

Ushbu misol orqali chiziq bo'yicha o'taylik:

  1. sinxronlash qo'riqchisining namunasini tezlashtirish
timeInSyncKeeper = yangi AsyncZoneTimeInSyncKeeper ();

2. apiService.query usuliga REQUEST_DELAY o'tganidan so'ng natija so'rovi bilan javob berishga imkon bering. So‘rov usuli sust va REQUEST_DELAY = 5000 millisekunddan keyin javob berishini aytaylik.

spyOn (apiService, 'query'). va.returnValue (Observable.of (queryResult) .yoshlash (REQUEST_DELAY));

3. Taklif qilingan maydonda "bo'sh emas" variant mavjudligini da'vo qiling

comp.options = ['bo'sh emas'];

4. Armaturaning ona elementidagi "kirish" maydoniga o'ting va "Lon" qiymatini kiriting. Bu foydalanuvchining kirish maydoni bilan o'zaro munosabatini taqlid qiladi.

SpecUtils.focusAndInput ('Lon', armatura, 'kiritish');

5. soxta asin zonasida DEBOUNCING_VALUE vaqtini o'tkazishga ruxsat bering (DEBOUNCING_VALUE = 300 millisekund).

timeInSyncKeeper.tick (DEBOUNCING_VALUE);

6. O'zgarishlarni aniqlang va HTML shablonini qayta namoyish eting.

fixture.detectChanges ();

7. Endi variantlar qatori bo'sh!

kutish (comp.options.length) .toBe (0, `[$ {comp.options.join (',')}]`) edi;

Bu shuni anglatadiki, tarkibiy qismlarda ishlatiladigan kuzatiladigan qiymat o'zgarishlari o'z vaqtida ishga tushirilgan. E'tibor bering, bajarilgan debounceTime-d funktsiyasi

qiymati => {
    this.options = [];
    this.onEvent.emit ({signal: SuggestSignal.start});
    this.suggest (value);
}

usul taklif qilish orqali navbatga boshqa vazifani itarib:

tavsiya qilish (q: string) {
    agar (! q) {
        qaytish;
    }
    this.googleBooksAPI.query (q). obunani bekor qilish (natija => {
        agar (natija) {
            this.options = result.items.map (element => item.volumeInfo);
            this.onEvent.emit ({signal: SuggestSignal.success, totalItems: result.totalItems});
        } else {
            this.onEvent.emit ({signal: SuggestSignal.success, totalItems: 0});
        }
    }, () => {
        this.onEvent.emit ({signal: SuggestSignal.error});
    });
}

5 soniyadan so'ng javob beradigan google kitoblari API so'rov usulini eslang.

8. Va nihoyat, mintaqa navbatini to'ldirish uchun yana REQUEST_DELAY = 5000 millisekundni tanlashimiz kerak. Biz taklif qiladigan usulda obuna bo'lgan kuzatuvni yakunlash uchun REQUEST_DELAY = 5000 kerak.

timeInSyncKeeper.tick (REQUEST_DELAY);

fakeAsync…? Nima uchun? Rejalashtiruvchilar bor!

ReactiveX mutaxassislari kuzatish mumkin bo'lgan narsalarni tekshirib ko'rish uchun biz sinovni rejalashtiruvchilardan foydalanishimiz mumkin deb bahslashishi mumkin. Bu burchakli dasturlar uchun mumkin, ammo ba'zi kamchiliklari bor:

  • kuzatuv tizimlarining ichki tuzilishi bilan tanishishingizni talab qiladi, operatorlar,…
  • Agar sizning ilovangizda yomon nosozliklar bo'lsa-chi? Ularni jadval tuzuvchilar ishlamaydi.
  • eng muhimi: men sizning butun ilovangizda rejalashtirgichlardan foydalanishni xohlamasligingizga amin emasman. Siz ishlab chiqarish kodini blok sinovlari bilan aralashtirmoqchi emassiz. Siz bunday narsani qilishni xohlamaysiz:
const testScheduler;
if (environment.test) {
    testScheduler = yangi YourTestScheduler ();
}
kuzatib turilsin;
if (testScheduler) {
    kuzatiladigan = kuzatiladigan.of ('qiymat'). kechikish (1000, testScheduler)
} else {
    kuzatiladigan = kuzatiladigan.of ('qiymat'), kechikish (1000);
}

Bu hayotiy echim emas. Menimcha, yagona mumkin bo'lgan echim - bu Rxjsning haqiqiy usullari uchun "ishonchli vakillarni" taqdim etish orqali sinov rejasini tuzish. Shuni hisobga olish kerak bo'lgan yana bir narsa shundaki, bekor qilish usullari jihozning qolgan sinovlariga salbiy ta'sir ko'rsatishi mumkin. Shuning uchun biz Yaseminning josuslaridan foydalanmoqchimiz. Har safar josuslar tozalashadi.

MonkeypatchScheduler funktsiyasi asl Rxjs amalga oshirilishini josus yordamida amalga oshiradi. Ayg'oqchi usulning dalillarini oladi va agar kerak bo'lsa, testScheduler dasturiga qo'shiladi.

'rxjs / Scheduler' dan {IScheduler} ni import qilish;
'rxjs / Observable' dan {Kuzatiladigan} ni import qilish;
var spyOn-ni e'lon qiling: funktsiya;
eksport funktsiyasi monkeypatchScheduler (rejalashtiruvchi: IScheduler) {
    allowableMethods = ['concat', 'key', 'boş', 'forkJoin', 'if', 'interval', 'birlashtirish', 'of', 'range', 'tashlash',
        'zip'];
    operatorMethods = ['bufer', 'tugatish', 'kechiktirish', 'aniq', 'qilish', 'har', 'oxirgi', 'birlashtirish', 'max', 'olish',
        'timeInterval', 'ko'tarish', 'debounceTime'];
    injectFn = funktsiya bo'lsin (asos: har qanday, usul: string []) {
        usullar.forEach (method => {
            const orig = tayanch [usul];
            if (typof orig === 'funktsiya') {
                spyOn (asos, usul) .and.callFake (funktsiya () {
                    let args = Array.prototype.slice.call (dalillar);
                    if (args [args.length - 1] && typeof args [args.length - 1] .now === 'funktsiya') {
                        args [args.length - 1] = rejalashtiruvchi;
                    } else {
                        args.push (rejalashtiruvchi);
                    }
                    return orig.apply (bu, args);
                });
            }
        });
    };
    injectFn (Kuzatiladigan, kuzatiladigan Metodlar);
    injectFn (Observable.prototype, operatorMetodlar);
}

Bundan buyon testScheduler Rxjs ichidagi barcha ishlarni bajaradi. U setTimeout / setInterval yoki asyncning har qanday turidan foydalanmaydi. FakeAsync-ga boshqa ehtiyoj qolmadi.

Endi, biz monkeypatchScheduler-ga o'tishni xohlagan sinov jadvaliga ehtiyoj sezamiz.

Bu odatiy TestScheduler kabi ishlaydi, ammo u onAction-da qo'ng'iroqni qaytarish usulini ta'minlaydi. Shu tarzda, qaysi vaqtdan keyin qaysi harakat bajarilganligini bilamiz.

SpyingTestScheduler eksport klassi VirtualTimeScheduler-ni kengaytiradi {
    spyFn: (actionName: satr, kechikish: raqam, xato?: har qanday) => bekor qilish;
    konstruktor () {
        super (VirtualAction, defaultMaxFrame);
    }
    onAction (spyFn: (actionName: string, keying: number, error?: any) => bekor) {
        this.spyFn = spyFn;
    }
    flush () {
        const {harakatlar, maxFrames} = bu;
        let error: any, action: AsyncAction ;
        while ((action = action.shift ()) && (this.frame = action.delay) <= maxFrames) {
            let stateName = this.detectStateName (action);
            let delay = action.delay;
            if (error = action.execute (action.state, action.delay)) {
                if (this.spyFn) {
                    this.spyFn (holatName, kechiktirish, xato);
                }
                sindirish;
            } else {
                if (this.spyFn) {
                    this.spyFn (holat nomi, kechiktirish);
                }
            }
        }
        if (xato) {
            while (action = action.shift ()) {
                action.unsubscribe ();
            }
            otish xatosi;
        }
    }
    shaxsiy detStateName (harakat: AsyncAction ): string {
        const c = Object.getPrototypeOf (action.state) .konstruktor;
        const argsPos = c.toString (). indexOf ('(');
        if (argsPos! == -1) {
            Qaytish c.toString (). pastki satr (9, argsPos);
        }
        return null;
    }
}

Va nihoyat, foydalanishni ko'rib chiqaylik. Bunga misol oldingi singari bir xil sinov (u ('avvalgi natijani o'chiradi')) va fakeAsync / tick o'rniga sinovni rejalashtirish vositasidan foydalanamiz.

let testScheduler;
oldinEach (() => {
    testScheduler = yangi SpyingTestScheduler ();
    testScheduler.maxFrames = 1000000;
    monkeypatchScheduler (testScheduler);
    fixture.detectChanges ();
});
oldinEach (() => {
    spyOn (apiService, 'query') va .callFake (() => {
        Observable.of-ni (queryResult) qaytaring. kechikish (REQUEST_DELAY);
    });
});
u ('oldingi natijani o'chiradi', (bajarilgan: Funktsiya) => {
    comp.options = ['bo'sh emas'];
    testScheduler.onAction ((actionName: satr, kechikish: raqam, xato?: har qanday) => {
        if (actionName === 'DebounceTimeSubscriber' && kechikish === DEBOUNCING_VALUE) {
            kutish (comp.options.length) .toBe (0, `[$ {comp.options.join (',')}]`) edi;
            amalga oshirildi ();
        }
    });
    SpecUtils.focusAndInput ('Londo', armatura, 'kiritish');
    fixture.detectChanges ();
    testScheduler.flush ();
});

Sinovlar jadvali birinchi EEach-da yaratiladi va (!) Montaj qilinadi. Oldingi ikkinchi darsda biz REQUEST_DELAY = 5000 millisekunddan keyin natija so'roviResultga xizmat qilish uchun apiService.query-ga josuslik qilamiz.

Endi bu chiziqdan chiziq bo'yicha o'taylik:

  1. Avvalo, biz testni rejalashtiruvchining qayta qo'ng'iroq qilish onAction bilan birgalikda zarur bo'lgan bajarilgan funktsiyani e'lon qilganimizni unutmang. Bu shuni anglatadiki, biz Yaseminga sinov o'z-o'zidan amalga oshirilishini aytib berishimiz kerak.
u ('oldingi natijani o'chiradi', (bajarilgan: Funktsiya) => {

2. Shunga qaramay, biz komponentda mavjud bo'lgan ba'zi variantlarni da'vo qilamiz.

comp.options = ['bo'sh emas'];

3. Bu ba'zi izohlarni talab qiladi, chunki u birinchi qarashda biroz xiralashgan ko'rinadi. Biz "DebounceTimeSubscriber" deb nomlangan harakatni DEBOUNCING_VALUE = 300 millisekundga kechiktirish bilan kutmoqchimiz. Bu sodir bo'lganda, biz options.length 0 ekanligini tekshirmoqchimiz. Keyin sinov tugadi va biz (() chaqiramiz.

testScheduler.onAction ((actionName: satr, kechikish: raqam, xato?: har qanday) => {
    if (actionName === 'DebounceTimeSubscriber' && kechikish === DEBOUNCING_VALUE) {
      kutish (comp.options.length) .toBe (0, `[$ {comp.options.join (',')}]`) edi;
      amalga oshirildi ();
    }
});

Ko'ryapsizmi, testni rejalashtiruvchilardan foydalanish Rxjsning ichki tuzilishi haqida ba'zi maxsus bilimlarni talab qiladi. Albatta, bu qaysi testni rejalashtirish vositasidan foydalanishingizga bog'liq, lekin siz o'zingiz kuchli jadvalni ishlab chiqsangiz ham, rejalashtiruvchilarni tushunishingiz va moslashuvchanlik uchun ba'zi ish vaqti qiymatlarini ochib berishingiz kerak (yana tushuntirish mumkin emas).

4. Yana foydalanuvchi "Londo" qiymatini kiritadi.

SpecUtils.focusAndInput ('Londo', armatura, 'kiritish');

5. Yana, o'zgarishlarni aniqlang va shablonni qayta namoyish eting.

fixture.detectChanges ();

6. Va nihoyat, biz jadval jadvaliga kiritilgan barcha amallarni bajaramiz.

testScheduler.flush ();

Xulosa

Angularning o'zlarining sinov dasturlari o'z-o'zidan ishlab chiqarilganlarga qaraganda afzalroqdir ... ular ishlamoqda. Ba'zi hollarda fakeAsync / malumotlarning juftligi ishlamaydi, ammo umidsizlikka tushirish va jihozni sinovdan o'tkazib yuborish uchun hech qanday sabab yo'q. Bunday holatlarda avtomatik sinxronlash yordam dasturi (bu erda AsyncZoneTimeInSyncKeeper deb ham nomlanadi) yoki maxsus testni rejalashtiruvchi (bu erda SpyingTestScheduler deb ham nomlanadi) bu yo'l.

Manba kodi