Today I Learned ... react.js
🙋 My Dev Blog
test
라는 이름의 항목이 존재한다.> yarn test
-파일명이 .test.js
로 끝나거나, (또는 .spec.js)
-src/__tests__
폴더에 작성된 js, jsx 파일들을 테스트함.
src/tests/Input.test.jsx
import React from "react";
import ReactDOM from "react-dom"; // React-DOM까지 임포트해야함.
import Input from "../component/Input";
describe("<Input>", () => { // describe 함수
it("renders without crashing", () => { // it 함수
const div = document.createElement("div");
ReactDOM.render(<Input />, div);
ReactDOM.unmountComponentAtNode(div); // unmountComponentAtNode()
});
});
1) describe()
describe keyword를 사용해서 작은 단위의 테스트 코드를 그룹화할 수 있다.2) it() / test()
작성한 테스트코드가 무엇을 하는지에 대해서 첫번째 param으로 작성해준다.
두번째 param으로는 테스트 통과시 실행할 코드. (여기선, Input을 div로 렌더링함)3) ReactDOM.unmountComponentAtNode()
DOM 노드가 마운트 된 후, 제거하고 이벤트 핸들러와 상태(state)를 정리하기 위해 사용
(뒷정리 함수라고 생각하면 됨.)
참고 - 마운트와 언마운트
마운트 | 언마운트 |
---|---|
받아온 props를 객체의 state로 설정 / setInteval, setTimeout 등 | 뒷정리 함수 / clearInterval 등 |
🔻 아래와 같이 warning msg가 뜬다.
Warning: Failed prop type: The prop `name` is marked as required in `Input`, but its value is `undefined`.
6 | it("renders without crashing", () => {
7 | const div = document.createElement("div");
> 8 | ReactDOM.render(<Input />, div);
| ^
9 | ReactDOM.unmountComponentAtNode(div);
10 | });
11 | });
위와 같이 jest를 이용한 yarn test
명령어로 테스트를 할 수 있다.
ReactDOM
함수는 테스트중인 컴포넌트 외에도 연결된 모든 컴포넌트를 출력하는 문제가 있다.> yarn add --dev enzyme enzyme-adapter-react-16.3 react-test-renderer
import { configure } from 'enzyme'; // 설정 함수 configure()
import Adapter from 'enzyme-adapter-react-16.3'; // 생명주기 라이브러리
configure({ adpater: new Adapter() }); // 새 버전 생명주기 함수와 함께 추가
src/tests/Input_spec.jsx
import React from "react";
import { shallow } from "enzyme"; // shallow() = 현재 컴포넌트의 생명주기만 테스트
// (자식 컴포넌트 생성, 출력은 건너뜀)
import Input from "../component/Input";
describe("<Input>", () => {
it("renders without crashing", () => {
expect(() => {
shallow(<Input />); // 오류 발생 안하면 테스트 통과
}).not.toThrow(); // toThrow() = 컴포넌트의 출력 오류를 검사.
});
});
참고문서 - jest 기본 사용법
예> shallow()가 반환하는 객체의 개수 검증시
const wrapper = shallow(<Input />);
expect(wrapper.length).toEqual(1); // toEqual()사용
expect(wrapper).toHaveLength(1); // toHaveLength() 사용
참고
yarn test 뒤에 파일경로 or 디렉터리 경로를 작성하면 해당 경로만 테스트 가능.> yarn test ./src/__tests__/Input_spec.jsx
➰ 교재 p.208-216 생략.
shallow() : 컴포넌트 출력 후 컴포넌트를 검사할 수 있는 enzyme 객체 반환
-> enzyme 객체는 find() 메서드를 포함함.
find() : 엘리먼트 출력 검사 (input, label등 엘리먼트를 인수로)
prop(), props() : 프로퍼티에 전달된 값 검증
-> 컴포넌트에 전달된 props가 의도대로 render()에서 할당되었는지 검사.
prop: enzyme가 반환한 컴포넌트의 프로퍼티 이름을 인자로 전달
props: 전체 프로퍼티 값을 객체로 반환
const wrapper = shallow(<Input />);
wrapper.setProps({ errorMsg: expectedErrorMessage });
wrapper.find('input').simulate('change', { target: {value: expectedTargetValue} });
-> target.value가 실제 브라우저가 전달하는 값이므로, 객체로 재현해야함.
import React, { PureComponent } from "react";
import PropTypes from "prop-types";
import withStyles, { css } from "./withStyles";
class CheckBox extends PureComponent {
constructor(props) {
super(props);
this.setRef = this.setRef.bind(this);
this.handleClick = this.handleClick.bind(this);
}
componentDidMount() {
if (this.props.autoFocus) {
this.ref.focus();
}
}
handleClick(e) {
const { name, onChange } = this.props;
onChange(name, e.target.checked);
}
setRef(ref) {
this.ref = ref;
}
render() {
const { errorMsg, label, children, styles, checked } = this.props;
return (
<label>
{label}
<div>
<input
ref={this.setRef}
type="checkbox"
checked={checked && "checked"}
onClick={this.handleClick}
/>
{children}
</div>
{errorMsg && (
<div>
<span {...css(styles.errorText)}>{errorMsg}</span>
</div>
)}
</label>
);
}
}
CheckBox.propTypes = {
name: PropTypes.string.isRequired,
autoFocus: PropTypes.bool,
checked: PropTypes.bool,
onChange: PropTypes.func,
};
CheckBox.defaultProps = {
autoFocus: false,
checked: false,
onChange: () => {},
};
export default withStyles(({ color, size }) => ({
errorText: {
fontSize: size.sm,
color: color.error,
},
}))(CheckBox);
import ThemedStyleSheet from 'react-with-styles/lib/ThemedStyleSheet';
import aphroditeInterface from 'react-with-styles-interface-aphrodite';
import { css, withStyles, withStylesPropTypes } from 'react-with-styles';
import Theme from './Theme';
ThemedStyleSheet.registerTheme(Theme);
ThemedStyleSheet.registerInterface(aphroditeInterface);
export { css, withStyles, withStylesPropTypes, ThemedStyleSheet };
export default withStyles;
참고 -
export
와export default
named export default export export class User {...} export default class User {...} import {User} from ... import User from ...