RxJS 대리석 테스트 초보자 가이드

RxJS 대리석 테스트는 내가 아주 오래 전에 처음 들은 화제다.그 이후로 나는 이것이 큰일이라는 느낌이 들었다. 테스트를 간소화할 수도 있지만, 왠지 모르게 나는 늘 미뤄졌다.의사에게 진찰받는 것만으로도 나를 어리석게 여겨 공부를 다음으로 미루기로 했다.내가 이 일을 이야기한 대다수 사람들은 모두 비슷한 느낌을 가지고 있다.
이 글의 목적은 너에게 탄주가 사람을 물지 않을 뿐만 아니라, 언뜻 보기에도 그렇게 어렵지 않다는 것을 보여주는 것이다.

문제 설명
우리의 코드 세션에서, 우리는 램프 스위치와 전구를 언급할 것이다.우리는 전구를 열고 닫을 수 있도록 코드를 작성할 것이다.우리는 각각 i를 사용하여 높은 상태를 표시하고 o를 사용하여 낮은 상태, 브리치true와false를 표시할 것이다.
  • true: 스위치 또는 전구 연결
  • false: 스위치 또는 램프 끄기

  • 전구를 켜다
    우리는 스위치를 모방하는 브리 함수와 전구를 밝히는 일련의 함수를 가지고 있다.매우 간단합니다.
    export function lightBulb(switch1$: Observable<boolean>): Observable<boolean> {
        return switch1$;
    }
    

    단원 테스트(탄주 없음)
    이제 함수가 예상대로 작동하는지 확인하기 위해 단원 테스트를 작성해 봅시다.탄주가 무서워서 우리는 이런 것을 썼다.
    test('should light the bulb when switch is on', (done) => {
        const switch$ = new Subject<boolean>();
    
        lightBulb(switch$).subscribe({
            next: (isLightOn) => {
                expect(isLightOn).toEqual(true);
                done();
            },
        });
    
        switch$.next(true);
    });
    
    이 테스트의 문제는 우리가 위에서 아래로 읽을 수 없다는 것이다.우선, 저희가 구독하면 lightBulb보상을 관찰할 수 있습니다.
    다음 몇 줄은 우리next의 주제이다.이것은 테스트 중간의subscribe 블록입니다. 우리는 단언하여 여기에 놓을 것입니다.이것은 틀린 것 같다.

    대리석류 테스트 설정
    RxJS 탄주로 우리의 함수를 테스트합시다.시작하기 전에 샘플 코드를 복사해서 테스트 파일에 넣어야 합니다.
    방주: 단언 부분은 우리가 RxJS 문서에서 본 것과 달리 여기의 Jest는 테스트 라이브러리로 사용된다.
    const testScheduler = new TestScheduler((actual, expected) => {
        expect(actual).toEqual(expected);
    });
    
    testScheduler 객체를 설치하고 있습니다.그것은 가상 시간을 사용하여 비동기 코드를 동기화할 수 있도록 할 것이다.

    탄주로 단원 테스트를 진행하다
    이제 우리는 첫 번째 대리석 테스트를 작성할 준비를 하고 있다.이전 테스트를 RxJS 대리석과 동일하게 재구성합니다.
    test('should light the bulb when switch is on', () => {
        testScheduler.run(({ hot, expectObservable }) => {
            const switch$ = hot('i', { i: true });
    
            const result$ = lightBulb(switch$);
    
            expectObservable(result$).toBe('i', { i: true });
        });
    });
    
    우리가 switch$ 대상에서run 방법을 실행하고 있다는 것을 알 수 있습니다.우리는 그것을 위해 testScheduler 대상 매개 변수를 받아들이는 리셋 함수를 제공했다.이 대상에서 우리는 대리석 테스트에 사용되는 함수를 묘사했다.
    다음에 우리가 helpers 관찰 대상을 만드는 방식은 다르다.현재 우리는 그것에 switch$의 실례를 분배하지 않고 Subjecthelper 함수를 사용한다. 이 함수는 helper의 대상에서 분해된 것이다. 왜냐하면 hot는 열로 관찰할 수 있기 때문이다.그리고 함수 조수로 냉관측을 만든다.Subject 함수의 매개 변수에서 우리는 흐름이 무엇을 되돌려야 하고 그것을 어떻게 되돌려야 하는지를 정의했다.우리는 흐르는 값을 정의하기 위해 문자열 대리석 그림과 디렉터리 대상을 사용합니다.우리의 예시에서, 우리는 그것이 높은 상태의 'i' (다른 알파벳일 수도 있음) 를 보내야 한다고 말했고, 두 번째 매개 변수에서, 우리는 'i' 는'true ', 즉 스위치를 켜는 것을 의미한다고 말했다.
    완성된 후에 우리는 관측 가능한 값을 매개 변수로 cold 함수에 전달할 것이다. 방법은 비대리석 테스트 버전과 같다.현재 우리는 함수가 되돌아오는 내용이 우리의 기대에 부합되는지 평가하는 방법이 필요하다.이를 위해 "expectObservable"함수를 사용합니다.여기에서 우리는'result$'이 되돌아오는 것은 하나의 흐름이어야 한다고 말한다. 그것은'i'를 보내고 그 값은 진짜일 것이다.

    테스트에 블록 추가
    보시다시피 우리가 첫 번째 대리석 테스트를 작성하는 방법은 그리 어렵지 않습니다. 이제 우리의 요구를 바꾸도록 하겠습니다.스위치를 10밀리초 동안 켜면 전구를 켜야 한다.만약 대리석이 없다면, 우리는 어쩔 수 없이 가짜 타이머와 기다림을 도입할 것이다.탄주가 얼마나 쉬운지 봐.
    test('should light the bulb after 10ms delay', () => {
        testScheduler.run(({ hot, expectObservable }) => {
            const switch$ = hot('i', { i: true });
    
            const result$ = lightBulb(switch$);
    
            expectObservable(result$).toBe('----------i', { i: true });
        });
    });
    
    우리는 코드 한 줄만 수정하고, 대시 10개를 추가했다.각 대시(및 문자)는 1ms를 나타냅니다.이것은 observable가 처음 10밀리초 동안 아무 일도 하지 않았다는 것을 의미한다. 그 다음에 hotlightBulb 을 보낼 것이다.만약 우리가 10개의 대시가 너무 지루하다고 생각한다면, 아래의 두 줄 코드는 서로 바꿀 수 있다.
    expectObservable(result$).toBe('----------i', { i: true });
    // equals to
    expectObservable(result$).toBe('10ms i', { i: true });
    
    우리의 테스트는 현재 실패할 것이기 때문에 i 함수를 업데이트해야 합니다.지금쯤 그럴 것이다.
    export function lightBulb(switch1$: Observable<boolean>): Observable<boolean> {
        return switch1$.pipe(delay(10));
    }
    
    이제 더 나아가자.우리는 두 개의 스위치와 전구가 있는 계단을 건설할 것이다. 스위치가 반대 위치에 있을 때 전구가 켜질 것이다.기본적으로 이것은 논리 연산이다.우리는 그것을 일깨워 주기 위해 진상표를 준비했다.

    두 개의 스위치가 모두 닫힌 위치에 있을 때, 등도 닫히고, 우리가 그 중 하나의 스위치를 켜진 위치로 전환할 때, 불이 켜진다.그리고 나서 우리가 다른 것을 켜자 불이 꺼졌다.우리의 코드 테스트는 아마 이럴 것이다.
    test('it should light the bulb ON and OFF if switches are switching', () => {
        testScheduler.run(({ hot, expectObservable }) => {
            const switch1$ = hot('---i---o---i', { i: true, o: false });
            const switch2$ = hot('-i---o---i--', { i: true, o: false });
            const expected$ = '   -i-o-i-o-i-o';
    
            const result$ = lightBulbWithStaircaseWiring(switch1$, switch2$);
    
            expectObservable(result$).toBe(expected$, { i: true, o: false });
        });
    });
    
    세 개의 대리석 그림이 있는데, 이것은 모든 관찰할 수 있는 물체를 대표한다.스위치 두 개와 전구 한 개.그것들은 수직으로 정렬되어 있기 때문에 예상한 내용과 시간을 더욱 쉽게 확정할 수 있다.시간은 왼쪽에서 오른쪽으로 밀고 나간다.1밀리초, 1밀리초.

    2밀리초 후(붉은 선으로 표시), true는 높은 상태로 설정되었고, lightBulb는 변하지 않았다(우리는 그것의 초기값이falsy라고 가정한다). 그래서 우리는 우리의 전구가 밝아지기를 바란다.
    4밀리초 후switch2$가 높은 상태switch1$로 설정되어 여전히 높은 상태에 있고 노란색 허선으로 표시됨) 이것이 바로 우리가 불을 끄기를 바라는 이유입니다.
    다음에 우리는 다른 스위치의 상태를 바꾸어 이런 식으로 추측한다.

    함수 업데이트
    좋습니다. 테스트는 여기 있습니다. 함수를 만들어서 실행합시다.
    export function lightBulbWithStaircaseWiring(switch1$: Observable<boolean>, switch2$: Observable<boolean>): Observable<boolean> {
        return combineLatest([switch1$.pipe(startWith(false)), switch2$.pipe(startWith(false))]).pipe(
            map(([s1, s2]) => s1 !== s2),
            skip(1)
        );
    }
    
    그것은 두 흐름을 한데 합친다.만약 그것들이 보낸 값이 다르면 되돌아오기switch1$, 그렇지 않으면 되돌아오기switch2$.우리는 모든 스위치가 움직일 때까지 기다리고 싶지 않아서 두 개의 스위치true를 하고 첫 번째 발사를 뛰어넘었다.
    비교를 위해 만약에 우리가 대리석을 사용하지 않는다면 테스트가 어떻게 될지 (차이는 말하지 않아도 알 수 있다):
    test('it should light the bulb ON and OFF if switches are switching', () => {
        const switch1$ = new Subject<boolean>();
        const switch2$ = new Subject<boolean>();
    
        const resultArray: boolean[] = [];
    
        jest.useFakeTimers();
    
        lightBulbWithStaircaseWiring(switch1$, switch2$).subscribe({
            next: (isLightOn) => {
                resultArray.push(isLightOn);
            },
        });
    
        switch1$.next(true);
        jest.advanceTimersByTime(1);
        switch2$.next(true);
        jest.advanceTimersByTime(1);
        switch1$.next(false);
        jest.advanceTimersByTime(1);
        switch2$.next(false);
        jest.advanceTimersByTime(1);
        switch1$.next(true);
        jest.advanceTimersByTime(1);
        switch2$.next(true);
    
        expect(resultArray).toEqual([true, false, true, false, true, false]);
    });
    

    약간의 건의
  • TestScheduler의 구조 함수 호출에서 제공한 리셋 함수의 단언에 단점을 두는 것이 유용할 수 있습니다.이렇게 하면 실제 객체와 예상 객체, 프레임에서 전송되는 내용과 방법을 시각적으로 비교할 수 있습니다.
  • 한 세트에 여러 개의 테스트를 작성할 때 매번 테스트를 하기 전에 테스트 계획 프로그램을 실례화하여 부작용을 추적하기 어렵지 않도록 한다.

  • 결론
    본문을 읽은 후에 RxJS 탄주에 대해 기본적인 이해를 얻었으면 합니다. 이제 RxJS 문서를 다시 방문하여 RxJS 탄주를 이해할 수 있습니다.
    이 문제를 한층 더 조사하다.너도 나의 리포를 볼 수 있다. 거기에서 본문에서 사용한 완전한 코드 예시와 기타 관측 가능한 데이터의 예시를 찾을 수 있다.

    도구책
    https://rxjs.dev/guide/testing/marble-testing
    https://en.wikipedia.org/wiki/Exclusive_or

    환매 협의
    https://github.com/ragtam/marbles-testing

    좋은 웹페이지 즐겨찾기