Skip to content

Week3 기술공유 : [빠르게 훓어보는 Testing React App]

Sungdong Jo edited this page Dec 21, 2019 · 2 revisions

조성동


단위, 통합, E2E Test

(* React 기준)

  • 단위 테스트 : 하나의 컴포넌트를 테스트

    • ex 1. state 값 변경 확인
    • ex 2. DOM check (HTML 태그, CSS style)
  • 통합 테스트 : 여러 컴포넌트들이 협력하는 동작을 테스트

    • ex 1. 부모 A 컴포넌트가 자식인 B 컴포넌트로 props를 잘 전달했는지
    • ex 2. state 변화에 따라 렌더링이 변경될 부분이 렌더링이 되었는지
  • E2E(End 2 End) 테스트 : 사용자 입장에서(실제 웹페이지) 컴포넌트들의 동작을 테스트

    • ex 1. Header의 존재 여부
    • ex 2. 버튼을 눌렀을 때 팝업의 열림 여부

Button Component


무엇을 테스트하지?

  • 마크업 및 스타일(색상, 크기, 여백, 커서 ...), 출력되는 텍스트, ... (너무 많다.)
  • Props, State
  • Event

// Button.tsx
function Btn({
  content,
  href,
  to,
  styletype = 'primary',
  ...props
}: Props): React.ReactElement {
if (to) {
  return (
    // react-router-dom/Link
    <S.StyledLink styletype={styletype} to={to} {...props}>
      {content}
    </S.StyledLink>
  );
}
if (href) {
  return (
    // a tag
    <S.Anchor styletype={styletype} href={href} {...props}>
      {content}
    </S.Anchor>
  );
}
return (
  // button tag
  <S.StyledBtn styletype={styletype} {...props}>
    {content}
  </S.StyledBtn>
);

Props

describe("Atom / Btn", () => {
  it("[PROPS] disabled", () => {
    // given
    const wrapperIsDisabled = mount(<Btn disabled={true}>iambutton</Btn>);
    const wrapperIsNotDisabled = mount(<Btn disabled={false}>iambutton</Btn>);

    // then
    expect(wrapperIsDisabled.prop("disabled")).toBe(true);
    expect(wrapperIsDisabled).toHaveStyleRule("cursor", "default");
    expect(wrapperIsDisabled).toHaveStyleRule("pointer-events", "none");

    expect(wrapperIsNotDisabled.prop("disabled")).toBe(false);
    expect(wrapperIsNotDisabled).toHaveStyleRule("cursor", "pointer");
    expect(wrapperIsNotDisabled).toHaveStyleRule("pointer-events", "auto");
  });
});

State

describe("Example state", () => {
  it("set and assert", () => {
    // given
    const wrap = shallow(<Component />);
    wrap.setState({ show: true });

    // then
    expect(wrap.state("show")).toEqual(true);
  });
});

Event

describe("Atom / Btn", () => {
  it("[EVENT] click", () => {
    const wrapper = mount(<Btn styletype="alert" />);

    // Btn 내부에 정의된 onClick이 호출
    wrapper.find("button").simulate("click");

    // do something...
  });
});

Snapshot 📸

이전에 촬영한 성공적인 컴포넌트 UI과 현재의 컴포넌트 UI를 비교한다. = 최종 마크업 비교


Snapshot 📸

describe("Atom / Btn", () => {
  it("[SNAPSHOT] StyledBtn", () => {
    // given
    const wrapper = mount(<Btn styletype={"primary"} />);

    // then
    expect(wrapper).toMatchSnapshot(); 📸
  });
});

Snapshot (Server)

// seed.spec.ts
// written by @FullOfOrange, @dobest27

describe("DB seed 데이터가 유효", () => {
  it("User 데이터가 유효", async () => {
    //given, when
    const users = await User.findAll({
      attributes: { exclude }
    });
    //then
    expect(users).toMatchSnapshot();
  });
});

Snapshot의 양면성

  • 테스트 작성 비용을 크게 줄인다.
  • TDD를 할 수 없다.
  • snapshot를 업데이트 하고 싶은 유혹이 있다. (테스트를 쉽게 통과할 수 있다.) 😏
  • UI는 정적이지 않다.

Snapshot test는 그럼 언제할까?

  • 자주 수정이 되지 않는 컴포넌트일 때
  • 너무 복잡하지 않은 컴포넌트일 때

ex. atomic component...


컴포넌트를 단위 테스트하는데 너무 많은 시간을 쏟지 마세요 😕



통합테스트를 많이 하자

  • 전부 다 테스트를 하자니 너무 많습니다(그리고 시간도 없습니다).
  • 세세한 테스트는 공감을 이끌어내지 못합니다. (노력 대비 쓸모가 없어보이기도 합니다.)

Q & A


BookUs!

개요
기획서

Tech

실용적인 리액트 테스트 전략
DevOps
Infra Structure
컴포넌트 작성법
Client Sturcture

Documents

데이터베이스 스키마
Yarn workspace 명령어
Docker를 이용한 서버 개발 환경
Linting Tools

Stress Testing Log

테스트 로그

1차 테스트

📝 Agile Process

스프린트 0주차: 기획 단계
스프린트 1주차: 개발 환경 구축
스프린트 2주차: 개발
스프린트 3주차: 개발
스프린트 4주차: 개발
스프린트 5주차: 개발
👉 스프린트 6주차 🔥

👷‍♂️ Technique Writing

🤝 Rules

Clone this wiki locally