React, TypeScript 및 TDD 섹션 2

36901 단어 reacttestingtypescript
React 구성 요소 개발이 재미있습니다.심지어음...재미있다테스트 내부에서 구성 요소 개발을 추진하다.
아니오, 진지해요.
우리가 《React+TDD》라는 책에서 보듯이 React+TDD는'질량'(공갈어록)과 채소를 먹는 것만은 아니다.특히 타자 스크립트와 스마트 도구가 결합될 때, 이것은 사람을 즐겁게 하는 발전 모델인 더 빨리, 즐겁게, 강아지이다.
본고에서 우리는 구성 요소 개발의 일부 모델을 이해함으로써 더욱 구체적으로 이해하도록 한다.알림으로 본고는 full video+text+code tutorial 중의 WebStorm Guide를 따른다.

TSX 및 ES6


React와 TypeScript를 사용하면 특히 스마트 편집기에서 좋은 JSX(TSX)와 ES6+ 지원을 의미한다.우리는 tutorial step 이 주제에 관한 문장에서 이 점을 볼 수 있다.
가령 우리가 몇 가지 코드를 가지고 있다면:
import React from "react";

function App() {
    return (
        <div>
            <h1>Hello React</h1>
        </div>
    );
}

export default App;
...그리고 그에 따른 테스트:
import React from "react";
import {render} from "@testing-library/react";
import App from "./App";

test("renders hello react", () => {
    const {getByText} = render(<App/>);
    const linkElement = getByText(/hello react/i);
    expect(linkElement).toBeInTheDocument();
});
그리고 우리는 몇 가지 테스트를 실행할 수 있다.이 예에서는 Jest test runner가 "스마트 편집기"에 통합되어 있는 것을 볼 수 있습니다WebStorm.

이제 TDD를 만들어 ES6 기능을 살펴 보겠습니다.

제목 추출


React에서 뭘 하고 있었어요(tm)?큰 어셈블리를 작은 어셈블리로 분해합니다.이 Heading 구성 요소에서 App 구성 요소를 추출하여 새로운 테스트를 시작합니다.물론 실패한 것은:
test("renders heading", () => {
  const { getByText } = render(<Heading />);
  const linkElement = getByText(/hello react/i);
  expect(linkElement).toBeInTheDocument();
});
우리는 심지어 우리의 구성 요소를 가져올 수 없다. 왜냐하면...그것은 존재하지 않는다.이제 추출Heading 구성 요소에 대한 첫 번째 시도를 적어 보겠습니다.
import React from "react";

export function Heading() {
    return <h1>Hello React</h1>;
}
테스트가 통과되면 입력에 새로운 테스트가 추가됩니다.

물론 하나의 구성 요소를 같은 파일에서 추출하는 것은 어느 정도에 React 커뮤니티가'파일마다 하나의 구성 요소'에 대한 준수를 위반하는 것이다구성 요소를 자체 파일Heading로 이동합니다.
export function Heading() {
  return <h1>Hello React</h1>;
}
...동료와 Heading.tsx:
import React from "react";
import {render} from "@testing-library/react";
import {Heading} from "./Heading";

test("renders heading", () => {
    const {getByText} = render(<Heading/>);
    const linkElement = getByText(/hello react/i);
    expect(linkElement).toBeInTheDocument();
});
이 파일에서 테스트를 실행하면 다시 통과합니다.

Heading.test.tsx 구성 요소를 가져오고 사용하려면 변경App.tsx이 필요합니다.
import React from "react";
import {Heading} from "./Heading";

function App() {
    return (
        <div>
            <Heading/>
        </div>
    );
}

export default App;
우리는 Heading 에서 테스트를 통과했다. App.test.tsx 하위 구성 요소에서 온 것을 몰랐다.
이제 부모 구성 요소와 하위 구성 요소의 테스트를 보여 줍니다.

도구와 유형


이것은 무료한 구성 부분이다.얘는 매번 똑같은 말을 해!'부모' 구성 요소가 '이름' 에 대한 값을 전달할 수 있도록 변경합니다.
우리는 먼저 Hello React에서 첫 번째 테스트를 작성했다.
test("renders heading with argument", () => {
  const { getByText } = render(<Heading name={`World`}/>);
  const linkElement = getByText(/hello world/i);
  expect(linkElement).toBeInTheDocument();
});
타자 스크립트와 도구 덕분에 우리는 더 빨리 실패했다. 즉각 빨간색 곡선으로 우리가 계약을 위반했다고 알려주었다.Heading.test.tsx 아직 이름 아이템을 사용하지 않았습니다.

Heading 구성 요소로 이동하여 수정합니다.
export function Heading({ name }) {
  return <h1>Hello {name}</h1>;
}
우리의 새로운 테스트가 통과되었다.이전의 테스트는 실패했다. Heading 통과되지 않았다.잘 처리할게요.
함수 매개 변수name는 어떻게 된 일입니까?이것은 매개 변수에서 필요한 값을 선택하는 쿨한 방법인 ES6object destructuring이다.
테스트를 통과했지만 TypeScript가 만족하지 않습니다.

우리는 아이템에 대한 어떤 정보도 가지고 있지 않습니다.온라인으로 유형 정보를 추가할 수 있습니다.
export function Heading({ name }: {name: string}) {
  return <h1>Hello {name}</h1>;
}
그러나 독립 {name} 또는 type에 배치하고 함수 매개 변수에 사용하는 것이 좋습니다.
type HeadingProps = { name: string };

export function Heading({ name }: HeadingProps) {
  return <h1>Hello {name}</h1>;
}
이제 첫 번째 테스트를 어떻게 복구하는지 살펴봅시다.

기본 아이템치


우리는 interface 도구를 받아들이기를 원하지만, 그것을 필요로 하지 않는다.Heading에 대한 정의가 변경된 것처럼 들리며 name를 옵션 필드로 표시합니다.
type HeadingProps = { name?: string };
그런 다음 객체 분해의 기본값인 다른 ES6 기능을 사용할 수 있습니다.
export function Heading({name = "React"}: HeadingProps) {
    return <h1>Hello {name}</h1>;
}
호출된 구성 요소가 type 제공되지 않으면 name Heading 을 속성 값으로 사용합니다.우리의 첫 번째 테스트React가 지금 통과되었다.
너는 또 누가 이 도구를 제공하지 않는지 아니?우리의 Heading.test.tsx 구성 요소.맞혀봐--우리가 App에서의 테스트가 지금 또 통과되었다.

개발 과정의 모든 단계에서 우리는'더 빨리 실패한다'는 것은 TypeScript와test first 덕분이다.더 좋은 것은 브라우저를 아직 보지 못했다는 것이다.우리는 흐르는 중이다.

아이템을 사용하여 구성 요소 분류


React 커뮤니티는 함수 프로그래밍과 순수한 함수 기반 구성 요소에 열광합니다.그러나 유형에 기초한 구성 요소 문법은 여전히 모든 고집쟁이들에게 적용된다.(방백: 그가 가리키는 것은 그 자신이다.)
클래스 기반 구성 요소로 작성할 수 있는 새로운 App.test.tsx 구성 요소를 만듭니다. 도구만 필요합니다.이 섹션과 일치하는 tutorial step 을 따라 추적할 것입니다.다음 절에서, 우리는 클래스에state를 도입할 것이다.
물론 실패한 Counter 테스트부터 시작하여 이 테스트는 테스트 라이브러리의 getByTestId 조회를 사용합니다.
import React from "react";
import {render} from "@testing-library/react";
import {Counter} from "./Counter";

test("should render a label and counter", () => {
    const {getByTestId} = render(<Counter/>);
    const label = getByTestId("counter-label");
    expect(label).toBeInTheDocument();
    const counter = getByTestId("counter");
    expect(counter).toBeInTheDocument();
});
Counter.test.tsx 파일을 만들었습니다.
import React, {Component} from "react";

export class Counter extends Component {
    render() {
        return (
            <div>
                <div data-testid="counter-label">Count</div>
                <div data-testid="counter">
          1
        </div>
            </div>
        );
    }
}
우리의 테스트가 통과되었다.그러나 이것은 매우 지루하다. 우리는 계수 옆에 표시된 라벨이 설정할 수 있고 학부모가 도구로 전달되기를 바란다.다음은 (실패한) 테스트입니다.
test("should render a counter with custom label", () => {
    const {getByTestId} = render(<Counter label={`Current`}/>);
    const label = getByTestId("counter-label");
    expect(label).toBeInTheDocument();
});
심지어 테스트를 실행하기 전에 TypeScript가 우리에게 계약을 위반했다고 알려주었기 때문에 실패했다.

실현으로 돌아가려면 두 가지가 필요하다. 하나는 도구의 Counter.tsx 정의이고, 그 다음은 도구를 사용하는 변경 클래스이다.
import React, {Component} from "react";

export type CounterProps = { label?: string };

export class Counter extends Component<CounterProps> {
    render() {
        const {label = "Count"} = this.props;
        return (
            <div>
                <div data-testid="counter-label">{label}</div>
                <div data-testid="counter">
                    1
                </div>
            </div>
        );
    }
}
우리type 테스트가 지금 통과되었다.우리는 클래스 기반 Counter 구성 요소가 하나 있는데, 이것은 도구를 받아들인다.

상태를 사용하여 어셈블리 분류


어느 정도는 "네, 저희"였지만 Counter없었어요...백작컨디션 있는 클래스 기반 구성 요소를 만듭니다.이 섹션은 tutorial step for Class Components With State와 일치합니다.
첫걸음이 뭐예요?알림: "best"와 압운합니다.그렇습니다. Counter 중 실패한 테스트부터 시작하겠습니다.
test("should start at zero", () => {
    const {getByTestId} = render(<Counter/>);
    const counter = getByTestId("counter");
    expect(counter).toHaveTextContent("0");
});
지금부터 실시한다.우리가 구성 요소 도구를 만들 때, 우리는 도구의 형상을 모의하기 위해 타자 스크립트 Counter.test.tsx 를 썼다.이 주도 마찬가지다.
export type CounterState = { count: number };
그런 다음 type 클래스를 지향으로 변경하여 다음 상태를 구현합니다.
export class Counter extends Component<CounterProps, CounterState> {
    state: CounterState = {
        count: 0,
    };

    render() {
        const {label = "Count"} = this.props;
        return (
            <div>
                <div data-testid="counter-label">{label}</div>
                <div data-testid="counter">
                    {this.state.count}
                </div>
            </div>
        );
    }
}
우리의 테스트가 통과되었다.state의 값은 클래스 변수로 완성되었습니다. 이것은 우리가 Counter에서 autocomplete를 얻었다는 것을 의미합니다.그러나 만약 우리가 숙제를 시도한다면, 우리는 React가 우리가 사용하지 않았다고 불평할 것이라는 것을 안다. this.state.count다행히 TypeScript에서 도움을 줄 수 있습니다.상태의 초기화를 모듈 범위로 이동한 다음 유형 정의를 변경합니다.
const initialState = {count: 0};
export type CounterState = Readonly<typeof initialState>;
우리의 수업은 현재 이 초기 상태를 가리키고 있다.
export class Counter extends Component<CounterProps, CounterState> {
    readonly state: CounterState = initialState;

    render() {
        const {label = "Count"} = this.props;
        return (
            <div>
                <div data-testid="counter-label">{label}</div>
                <div data-testid="counter">
                    {this.state.count}
                </div>
            </div>
        );
    }
}
우리의 테스트는 여전히 통과되었다.마찬가지로 이것은 테스트 구동 개발의 장점이기도 하다. 당신은 자신감 있게 변경할 수 있고 도구에 유지할 수 있다.
카운터의 시작 값을 도구로 전송할 수 있도록 변경합니다.먼저 실패한 테스트:
test("should start at another value", () => {
    const {getByTestId} = render(<Counter/>);
    const counter = getByTestId("counter");
    expect(counter).toHaveTextContent("10");
});
테스트가 실패했을 뿐만 아니라 TypeScript는 테스트가 실행되기 전에 우리에게 계약을 외쳤다.

도구의 유형 정의를 변경해야 합니다.
export type CounterProps = {
    label?: string;
    start?: number;
};
이것만 있으면 우리는 전화setState로 이 값을 업데이트할 수 있다.우리는 라이프 사이클 방법을 채택할 것이다.
componentDidMount() {
    if (this.props.start) {
      this.setState({
        count: this.props.start,
      });
    }
  }
우리의 테스트는 지금 통과되었다.계수기는 기본 시작 계수를 가지고 있지만 도구로 전송된 새로운 계수를 받아들일 수 있습니다.

결론


우리는 이 세 가지 절차에서 많은 내용을 소개했다. ES6의 세부 사용,props와state의 유형 정의, 클래스 기반 구성 요소의 사용.이 모든 것은 브라우저에 접근할 필요가 없습니다.
세 번째 부분도 마지막 부분에서 우리는 이벤트 처리 프로그램을 연결하여 더욱 스마트한 부모/하위 구성 요소로 재구성할 것이다.우리는 타자 스크립트와 테스트가 우리가 더욱 빨리 실패하는 것을 도울 수 있도록 하는 방식으로 이 두 가지 일을 할 것이다

좋은 웹페이지 즐겨찾기