Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
Tags
- alexnet
- cors
- 이진탐색
- 해시테이블
- 알고리즘
- GraphQL
- 웹팩
- 연결리스트
- 브라우저
- 프로그래머스
- APOLLO
- 컨테이너
- 스택
- 포인터
- 릿코드
- vue3
- RxJS
- 연결 리스트
- 자료구조
- 코딩테스트
- 배열
- 프로세스
- 자바스크립트
- 큐
- 타입스크립트
- C
- RT scheduling
- 프론트엔드
- pytorch
- Machine Learning
Archives
- Today
- Total
프린세스 다이어리
[RxJS] 테스트코드 짜는 방법 본문
728x90
Mocha.js와 RxJS 테스트 도구를 통해 비동기 코드를 어떻게 테스트하는지 알아본다. <RxJS 반응형 프로그래밍> 책 내용 재정리
1. 동기 함수 테스트
순수함수 특징
- 범위가 작고, 명확하게 정의된 매개변수 세 개정도를 가짐.
- 예측 가능하고 일관된 출력을 냄
- 전달된 인수로부터 결과가 바로 결정되는 결정론적인 특성이 있어, 테스트 결과는 인수에 달려 있음.
export const isNotEmpty = (input) => {
return !!input && input.trim().length > 0;
};
import { expect } from "chai";
import { isNotEmpty } from "../9_1.js";
describe("기본적인 동기 테스트 코드", () => {
it("간단한 빈문자열 밸리데잇 함수", () => {
expect(isNotEmpty("나는 이은진")).to.be.equal(true);
expect(isNotEmpty(" ")).to.be.equal(false);
expect(isNotEmpty(null)).to.be.equal(false);
expect(isNotEmpty(undefined)).to.be.equal(false);
});
});
- it으로 묶인 테스트 블록들은 하나의 특정 작동의 특성을 지니도록 작성한다.
- 긍정적, 부정적인 사용 사례에 대해 어서션 테스트를 한다. (* 어서션: 참거짓을 미리 가정함)
2. 비동기 함수 테스트
describe("검색결과 비동기 요청 테스트 코드 - should 추가", function () {
it("위키피디아에 'reactive programming' 키워드 검색", function (done) {
this.timeout(2000);
const searchTerm = "reactive+programming";
const url = `https://en.wikipedia.org/w/api.php?action=query`+
`&format=json&list=search&utf8=1&srsearch=${searchTerm}`;
const success = (results) => {
// 성공 함수 설정
expect(results)
.to.have.property("query")
.with.property("search")
.with.length(10);
done();
};
const error = (err) => {
done(err);
};
getData2(url, success, error);
});
});
- done함수를 전달하면 Mocha가 중지되고 비동기 함수가 반환될 때까지 기다림.
- done 함수를 포함하는 success, error 함수를 인자로 전달
describe("검색결과 비동기 요청 테스트 코드 - should 추가", function () {
it("위키피디아에 'reactive programming' 키워드 검색", function () {
// done함수를 사용하는 대신 Promise를 Mocha로 반환함
const searchTerm = "reactive+programming";
const url = `https://en.wikipedia.org/w/api.php` +
`?action=query&format=json&list=search&utf8=1&srsearch=${searchTerm}`;
return getData3(url)
.should.be.fulfilled
.should.eventually.have.property("query")
.with.property("search")
.with.length(10);
});
});
- should.js api를 사용하여, should.be..fulfilled 처럼 promise 최종값에 어서션 테스트 진행
- done을 통과하는 대신, mocha가 테스트 중인 promise 객체를 엔진에 반환하여, 지정된 기댓값을 충족하길 기대한다.
3. 반응형 스트림 테스트
describe("rxjs 연산자로 구성된 코드의 테스트를 짜봅시다", () => {
it("동기 - 더하기 연산", () => {
of(1, 2, 3, 4, 5, 6, 7, 8, 9)
.pipe(reduce((total, delta) => total + delta))
.subscribe((total) => {
expect(total).to.equal(45);
});
});
});
- 동기 옵저버블 테스트
describe("rxjs 연산자로 구성된 코드의 테스트를 짜봅시다", () => {
it("1000ms 딜레이 - 더하기 연산", () => {
from([1, 2, 3, 4, 5, 6, 7, 8, 9])
.pipe(
reduce((total, delta) => total + delta),
delay(1000)
)
.subscribe((total) => {
expect(total).to.equal(45);
});
});
it("1000ms 딜레이 - 더하기 연산", () => {
from([1, 2, 3, 4, 5, 6, 7, 8, 9])
.pipe(
reduce((total, delta) => total + delta),
delay(1000)
)
.subscribe((total) => {
expect(total).to.equal("성공한 거 맞아요?");
// 에러는 발생하는데, 테스트는 통과한 것으로 나온다!!
// 비동기 블록 실행이 완료되기도 전에 테스트가 완료되었다고 보고되는 게 문제.
});
});
});
- 비동기 옵저버블 잘못된 예시
4. 스트림을 테스트에 용이하게 리팩토링하기
/**
* 리팩토링 전 프로그램
*/
interval(200)
.pipe(
take(10),
filter((num) => num % 2 === 0),
map((num) => num * num),
reduce((total, delta) => total + delta)
)
.subscribe(console.log);
- 옵저버블 파이프라인에서 비즈니스 로직 분리
- 소비자와 생산자 분리, 스트림 파이프라인 격리. 어서션 코드 주입 위함
- 스트림을 적절한 옵저버로 호출할 수 있는 함수로 래핑
/**
* 리팩토링 후 프로그램
*/
const isEven = (num) => num % 2 === 0;
const square = (num) => num * num;
const add = (total, delta) => total + delta;
export const runInterval = (source$) =>
source$.pipe(take(10), filter(isEven), map(square), reduce(add));
- 인수를 만들어 비즈니스 로직에서 생산자(소스)와 구독자를 분리해줌.
- 래핑 결과, 테스트 입력 인수를 전달할 수 있게 됨.
- 코드 전체 경로에서 실행하는 데 요구되는 가능한 사용사례를 전부 감당할 수 있는 유연성 가짐 - ?
/**
* 테스트에 최적화하여 리팩토링
*/
describe("옵저버블 테스트", function () {
it("부가작용 없는 옵저버블 리팩토링", (done) => {
this.timeout(2000); // 스트림을 완료할 수 있게 Mocha의 시간 초과 설정 늘림.
runInterval(interval(200)).subscribe({
next: (total) => expect(total).to.equal(120),
err: (err) => assert.fail(err.message),
complete: done(),
});
// 기댓값은 스트림 코드에서 분리되어, 테스트에 연결됨
});
});
- 이렇게 하면 테스트 기본 시간(2000ms)를 초과할 것이라고 Mocha에게 항상 알려야 함.
- interval 같은 명시적인 시간값이 존재하는 코드에서도 테스트 속도를 빠르게 하려면 스케줄러 추가가 필요.
5. RxJS의 값 스케줄링
얄코 RxJS 스케줄러 관련글: https://www.yalco.kr/@rxjs/1-5/
it("동기 스케줄링", () => {
let stored = [];
let store = (state) => stored.push(state);
let scheduler = queueScheduler;
scheduler.schedule(store(1));
scheduler.schedule(store(2));
scheduler.schedule(store(3));
scheduler.schedule(store(4));
scheduler.schedule(store(5)); // schedule을 호출할 때마다 Subscription 객체 반환
scheduler.flush();
expect(stored).to.deep.equal([1, 2, 3, 4, 5]);
});
- 동기 스케줄링
it("비동기 스케줄러에 값 발행하기", (done) => {
let temp = [];
of(1, 2, 3, 4, 5)
.pipe(observeOn(asyncScheduler), tap([].push.bind(temp)))
.subscribe({
next: (value) => {
console.log(value);
expect(temp).to.have.length(value);
expect(temp).to.contain(value);
},
error: (err) => console.error(err),
complete: done(),
});
});
- 비동기 스케줄러를 사용하도록 스트림을 구성하여 생산자가 방출한 값을 프록시함.
- 구독 블록 전에 모든 값이 방출되게 함
- 값을 방출하는 모든 비동기 부분에서 배열이 증가하에 어서션 테스트 수행
728x90
'FE' 카테고리의 다른 글
DOM이 무엇인가? Virtual DOM이란? 리액트에서의 DOM은? (0) | 2022.05.17 |
---|---|
[RxJS] 중첩 Observable을 처리하는 3가지 방법 - mergeMap(), concatMap(), switchMap() (0) | 2022.02.28 |
[RxJS] Observable 스트림의 흐름을 합치는 3가지 방법 merge(), concat(), switch() (1) | 2022.02.27 |
프로미스 후속 처리 메서드(resolve, reject, all, race, allSettled) 정리 (0) | 2022.01.29 |
[Storybook] 스토리북 mdx 기본틀 잡기 (0) | 2022.01.28 |
Comments