Jest: Jest for react-native๋Š” ๋ชจ๋“  props๋ฅผ ์ธ์‹ํ•˜์ง€ ๋ชปํ•ฉ๋‹ˆ๋‹ค.<touchablehighlight/>

์— ๋งŒ๋“  2016๋…„ 09์›” 16์ผ  ยท  3์ฝ”๋ฉ˜ํŠธ  ยท  ์ถœ์ฒ˜: facebook/jest

๋‚ด ์‚ฌ์šฉ์ž ์ •์˜ ๋ฒ„ํŠผ์— ๋Œ€ํ•œ ํด๋ž˜์Šค๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

import React, { Component } from 'react';
import { StyleSheet, View, TouchableHighlight, Text, Image } from 'react-native';

class NavBarBackButton extends Component {

  static propTypes = {
    onClick: React.PropTypes.func.isRequired,
    buttonText: React.PropTypes.string,
    customStyle: React.PropTypes.object
  };

  render() {

    return (
      <TouchableHighlight
        style={[styles.backButton, this.props.customStyle]}
        onPress={() => {this.props.onClick()}} >
        <View style={{flexDirection: 'row'}}>
          <Image
            style={styles.backImage}
            source={require('../img/back-128.png')}
          />
          <Text style={styles.backButtonText}>
            {this.props.buttonText}
          </Text>
        </View>
      </TouchableHighlight>
    );
  }
}

const styles = StyleSheet.create({
  backButton: {
    justifyContent: 'center'
  },
  backButtonText: {
    fontSize: 17,
    textAlignVertical: 'center',
    color: 'black',
  },
  backImage: {
    width: 40,
    height: 40
  },
});

export default NavBarBackButton;

์ด ํ…Œ์ŠคํŠธ๋กœ ์Šค๋ƒ…์ƒท์ด ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค.

import 'react-native';
import React from 'react';
import NavBarBackButton from '../navBarBackButton';

import renderer from 'react-test-renderer';

function doWork() {
  console.log('Back button was clicked!');
}

it('renders all UI elements', () => {

  const tree = renderer.create(
    <NavBarBackButton onClick = {doWork}/>
  ).toJSON();

  expect(tree).toMatchSnapshot();
});

ํ•˜์ง€๋งŒ <TouchableHighlight/> ์š”์†Œ์— ๋‹ค๋ฅธ ์†Œํ’ˆ์„ ์ถ”๊ฐ€ํ•˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋ฉ๋‹ˆ๋‹ค.

underlayColor={'blue'}

๊ทธ๋ฆฌ๊ณ  ํ…Œ์ŠคํŠธ๋ฅผ ๋‹ค์‹œ ์‹คํ–‰ํ•˜์‹ญ์‹œ์˜ค - ์Šค๋ƒ…์ƒท์ด ์ด ์ถ”๊ฐ€ ์†Œํ’ˆ์„ ๊ธฐ๋Œ€ํ•˜์ง€ ์•Š์•˜๋‹ค๋Š” ๊ฒƒ์„ ๋ณด์—ฌ์ฃผ๋Š” diff๋ฅผ ์–ป์ง€ ๋ชปํ–ˆ์Šต๋‹ˆ๋‹ค. ์‚ฌ์‹ค, ์œ ์ผํ•œ ์†Œํ’ˆ์€ ์†”๊ธฐ๋ฅผ "์Šคํƒ€์ผ"๋กœ ํ‰๊ฐ€ํ–ˆ์Šต๋‹ˆ๋‹ค.
์ด ์ž‘์—…์ด ์ง„ํ–‰ ์ค‘์ž…๋‹ˆ๊นŒ?
๋˜ํ•œ ๊ด€๋ จ ๋ฉ”๋ชจ์—์„œ TouchableHighlight์˜ ์œ ํ˜•์ด ์Šค๋ƒ…์ƒท(๋ฐ ๋””๋ฒ„๊ฑฐ)์— 'TouchableHightlight'๊ฐ€ ์•„๋‹Œ 'View'๋กœ ํ‘œ์‹œ๋˜๋Š” ์ด์œ ๋Š” ๋ฌด์—‡์ž…๋‹ˆ๊นŒ?

๋‚ด package.jason์—๋Š” ๋‹ค์Œ์ด ์žˆ์Šต๋‹ˆ๋‹ค.

{
  "name": <my_project_name>,
  "version": "0.0.1",
  "private": true,
  "scripts": {
   "start": "node node_modules/react-native/local-cli/cli.js start",
    "test": "jest"
  },
  "jest": {
    "preset": "jest-react-native",
    "collectCoverage": true,
    "verbose": true,
    "preprocessorIgnorePatterns": [
      "node_modules/(?!react-native|<my_project_name>|react-native-button)"
    ]
  },
  "dependencies": {
    "lodash": "^4.15.0",
    "react": "^15.3.1",
    "react-native": "^0.33.0",
  },
  "devDependencies": {
    "babel-jest": "^15.0.0",
    "babel-polyfill": "^6.13.0",
    "babel-preset-react-native": "^1.9.0",
    "jest": "^15.1.1",
    "jest-cli": "^15.1.1",
    "jest-react-native": "^15.0.0",
    "react-addons-test-utils": "^15.2.1",
    "react-shallow-testutils": "^2.0.0",
    "react-test-renderer": "^15.3.1"
  }
}

๋ฃจํŠธ ๋””๋ ‰ํ† ๋ฆฌ์˜ ๋‚ด .babelrc๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

{
  "presets": ["react-native"]
}

๊ฐ€์žฅ ์œ ์šฉํ•œ ๋Œ“๊ธ€

@cpojer ๋น ๋ฅธ ๋‹ต๋ณ€ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค! ์ด์ œ ๋‚ด ์Šค๋ƒ…์ƒท์ด ํ›จ์”ฌ ๋” ๊นจ๋—ํ•ด ๋ณด์ž…๋‹ˆ๋‹ค.
์ถ”๊ฐ€ ์งˆ๋ฌธ - ํ˜„์žฌ ๋‚ด ์ถœ๋ ฅ์€ ์ „๋‹ฌ๋œ ํ•จ์ˆ˜๋กœ onPress prop์„ ์ง€์ •ํ•˜๋Š” ๋ผ์ธ์„ ๋‹ค๋ฃจ์ง€ ์•Š๋Š”๋‹ค๊ณ  ๋ถˆํ‰ํ•ฉ๋‹ˆ๋‹ค.

static propTypes = {
    onClick: React.PropTypes.func.isRequired,
    ...
};

<TouchableHighlight
    onPress={() => {this.props.onClick()}} > //no coverage for this line of code in test
    ...
</TouchableHighlight>

๋ฒ„ํŠผ ํด๋ฆญ์„ ์‹œ๋ฎฌ๋ ˆ์ด์…˜ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์•Œ๊ณ  ์žˆ์Šต๋‹ˆ๊นŒ? ์•„๋‹ˆ๋ฉด ๋‚ด ํ…Œ์ŠคํŠธ๊ฐ€ onPress={() => {this.props.onClick()}} > ์ปค๋ฒ„ํ•˜๋„๋ก ํ•˜๋ ค๋ฉด ๋ฌด์—‡์ด๋“  ํ•˜์‹œ๊ฒ ์Šต๋‹ˆ๊นŒ? ์ฃผ์ œ์— ๋Œ€ํ•œ ์ž์Šต์„œ์—์„œ ์•„๋ฌด ๊ฒƒ๋„ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

๋ฏธ๋ฆฌ ๊ฐ์‚ฌ๋“œ๋ฆฝ๋‹ˆ๋‹ค!

๋ชจ๋“  3 ๋Œ“๊ธ€

์šฐ๋ฆฌ๋Š” ํŠน๋ณ„ํžˆ TouchableHighlight๋ฅผ ์กฐ๋กฑํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๊ทธ๊ฒƒ์ด ๋ฐ˜์‘ ๋„ค์ดํ‹ฐ๋ธŒ๊ฐ€ ๋ Œ๋”๋งํ•˜๋Š” ๊ฒƒ์ด๊ธฐ ๋•Œ๋ฌธ์— View๋กœ ๋ Œ๋”๋ง๋ฉ๋‹ˆ๋‹ค.

TouchableHighlight๋Š” ๊ธฐ๋ณธ ์ง€์› ์š”์†Œ( https://github.com/facebook/react-native/blob/master/Libraries/Components/Touchable/TouchableHighlight.js#L200) ์— ์ง์ ‘ ์†Œํ’ˆ์„ ์„ค์ •ํ•˜๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ๋ณด์ด๋ฉฐ ํŠน๋ณ„ํ•œ ๋ชจ์˜๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ๋ Œ๋”๋ง๋œ ์ถœ๋ ฅ์— ์†Œํ’ˆ์ด ํ‘œ์‹œ๋˜๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.

์ž‘๋™ํ•  ์ˆ˜ ์žˆ๋Š” ํ•œ ๊ฐ€์ง€๋Š” ์›น ์‚ฌ์ดํŠธ์˜ ๋‘ ๋ฒˆ์งธ ์˜ˆ์ œ์—์„œ ์ œ์•ˆํ•œ ๊ฒƒ์ฒ˜๋Ÿผ ์ž์‹ ๋งŒ์˜ ๋ชจ์˜๋ฅผ ๋งŒ๋“œ๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. http://facebook.github.io/jest/docs/tutorial-react-native.html#mock -native-modules- ์กฐ๋กฑ์„ ์‚ฌ์šฉํ•˜๋‹ค

jest.mock('TouchableHighlight', () => {
  const jestReactNative = require('jest-react-native');
  return jestReactNative.mockComponent('TouchableHighlight');
});

@cpojer ๋น ๋ฅธ ๋‹ต๋ณ€ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค! ์ด์ œ ๋‚ด ์Šค๋ƒ…์ƒท์ด ํ›จ์”ฌ ๋” ๊นจ๋—ํ•ด ๋ณด์ž…๋‹ˆ๋‹ค.
์ถ”๊ฐ€ ์งˆ๋ฌธ - ํ˜„์žฌ ๋‚ด ์ถœ๋ ฅ์€ ์ „๋‹ฌ๋œ ํ•จ์ˆ˜๋กœ onPress prop์„ ์ง€์ •ํ•˜๋Š” ๋ผ์ธ์„ ๋‹ค๋ฃจ์ง€ ์•Š๋Š”๋‹ค๊ณ  ๋ถˆํ‰ํ•ฉ๋‹ˆ๋‹ค.

static propTypes = {
    onClick: React.PropTypes.func.isRequired,
    ...
};

<TouchableHighlight
    onPress={() => {this.props.onClick()}} > //no coverage for this line of code in test
    ...
</TouchableHighlight>

๋ฒ„ํŠผ ํด๋ฆญ์„ ์‹œ๋ฎฌ๋ ˆ์ด์…˜ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์•Œ๊ณ  ์žˆ์Šต๋‹ˆ๊นŒ? ์•„๋‹ˆ๋ฉด ๋‚ด ํ…Œ์ŠคํŠธ๊ฐ€ onPress={() => {this.props.onClick()}} > ์ปค๋ฒ„ํ•˜๋„๋ก ํ•˜๋ ค๋ฉด ๋ฌด์—‡์ด๋“  ํ•˜์‹œ๊ฒ ์Šต๋‹ˆ๊นŒ? ์ฃผ์ œ์— ๋Œ€ํ•œ ์ž์Šต์„œ์—์„œ ์•„๋ฌด ๊ฒƒ๋„ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

๋ฏธ๋ฆฌ ๊ฐ์‚ฌ๋“œ๋ฆฝ๋‹ˆ๋‹ค!

@rosiakr ๋„ค, ๋ฒ„ํŠผ ํด๋ฆญ ์ด๋ฒคํŠธ๋ฅผ ํ…Œ์ŠคํŠธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
๋‹ค์Œ์€ ๋ฒ„ํŠผ ํด๋ฆญ์„ ํ…Œ์ŠคํŠธํ•˜๊ธฐ ์œ„ํ•ด ์ž‘์„ฑํ•œ ์ฝ”๋“œ์ž…๋‹ˆ๋‹ค.

class YourComponent extends Component {
  handleButtonClick = () => {};

  render() {
    return <Button transparent onPress={() => this.handleButtonClick()}></Button>
  }
}

//jest
it("Test Component", () => {
  const component = renderer.create(
    <YourComponent/>
  );

  // manually trigger the  button click.
  component.getInstance().handleButtonClick();
};
์ด ํŽ˜์ด์ง€๊ฐ€ ๋„์›€์ด ๋˜์—ˆ๋‚˜์š”?
0 / 5 - 0 ๋“ฑ๊ธ‰