• javascript 테스트를 도와 주는 라이브러리
  • Spies, Stubs, Mocks... 등의 기능을 제공
  • java의 Mockito와 기능이 비슷함

Spies

  • 특정 메소드가 몇 번 호출/ 파라미터 / 리턴되는 결과값이 몇번 호출되었는지를 추적이 가능. 타겟의 뒤를 추적하는 영화에서 나오는 스파이를 생각하면 될듯하다.

  • 익명 함수 또는 기존에 메소드에 대해서 spy 기능을 사용할 수 있다.

  • 기존 메소드에 spy를 사용할 경우 기능에 영향을 주지는 않는다 단순 추적만 가능

it("array", () => {
  const targetArr = [];
  const spyPush = sinon.spy(targetArr, "push");
  targetArr.push(1);
  targetArr.push(2);
  // expect(spyPush).to.have.been.calledOnce; // err
  expect(spyPush).to.have.been.calledTwice; // ok
});

stubs

  • spy에 기능이 추가되었다.
  • spy와 다르게 기존 기능을 다르게 동작하도록 할수 있다.
  • 시간이 오래 걸리는 작업 또는 외부에서 데이터를 가져와야 하는 경우 등의 환경에서 미리 응답값을 정의해서 해당 메소드가 동작을 한 것처럼 조작이 가능하다.
it("array", () => {
  const targetArr = [];
  const stubPop = sinon.stub(targetArr, "pop").returns("null");
  targetArr.push(1);
  targetArr.push(2);

  // 2, 1이 출력되어야 하지만 stub때문에 null, null 이 출력된다.
  console.log(targetArr.pop());
  console.log(targetArr.pop());
});

비동기 테스트

  • stub의 usingPromise 메소드를 사용
const service = {
  login(id, password) {
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        if (id === "error") {
          reject("network error");
        } else if (id === "root" && password === "root123") {
          resolve({ isOK: true });
        } else {
          resolve({ isOK: false });
        }
      }, 3000);
    });
  }
};

const app = {
  service: service,
  login(id, password) {
    if (this.validate(id, password)) {
      this.service.login(id, password).then(result => {
        if (result.isOK) {
          this.movePage("main");
        } else {
          utils.log("Login result fail");
        }
      });
    } else {
      utils.log("Invalidated id/password");
    }
  },
  ...
}

/// 테스트
  it("test login success", done => {
    let spyMovePage = sinon.spy(app, "movePage");

    let spyLogin = sinon
      .stub(app.service, "login")
      .usingPromise(Promise)    // login 메소드에서 리턴되는 타입
      .withArgs("test", "test123") // test, test123으로 로그인시 결과는 OK 되도록 정의
      .resolves({ isOK: true });

    app.login("test", "test123");

    spyLogin().then(() => {
      expect(spyMovePage).to.have.been.calledOnce; // ok
      expect(spyMovePage).to.have.been.calledWith("main"); // ok
      // expect(spyMovePage).to.have.been.calledWith("login"); // error
      done();
    });
  });

Mocks

  • spy + stub 의 기능이 섞여 있다. (공식 홈페이지를 봐도 헷갈림)
  • 현재 이해한 것은 mock 대상 메소드가 예상 시나리오대로 실행되는지를 검증하는거 같다.
let mock = sinon.mock(app);
mock
  .expects("validate")
  .withArgs("test", "test123")
  .returns(false);

app.login("test", "test123");
mock.verify();

mock 사용에서 처음 혼동되는 것은 위 예제처럼 test/test123을 사용 할경우 리턴되는 값이 false가 되는지를 검증하는 것으로 이해를 했지만 withArgs 에서 사용 한 값에 대해서만 검증을 하고 리턴되는 값은 검증 대상에 포함이 되지 않는다. 로그인에 test123을 사용하면 에러지만 리턴되는 값은 true를 설정해도 정상 처리 된다. 이부분은 메소드 체이닝 때문에 혼동이 된듯싶다. Mock API 참고 필요

주의 사항

  • spy/stub/mock 등 Sinon기능 사용이 끝나면 restore() 메소드를 호출하여 원본 메소드 상태로 되돌려야 한다. 그렇지 않을 경우 다른 테스트에도 영향을 준다. 또는 after / afterEach 메소드에서 초기화를 해주도록 하자 또한 spy/stub/mock 메소드별 초기화 메소드가 있으니 참고한다. (대부분 reset으로 시작함)
  • https://sinonjs.org/releases/v7.4.1/general-setup/

참고

'JS' 카테고리의 다른 글

babel-webpack 설정  (0) 2020.07.17
babel setting  (0) 2020.07.14
Mocha-Sinon 개발 환경  (0) 2019.08.20
[Vuejs] mocha+chai+sinon 테스트  (0) 2019.08.13
Static server  (0) 2019.08.05

+ Recent posts