본문 바로가기
IT/React

RN(ReactNative) TDD TODO 앱 만들기 #5

by DOSGamer 2022. 8. 12.
반응형

2022.08.11 - [IT/React] - RN(ReactNative) TDD TODO 앱 만들기 #4

TaskList 에 있는 체크박스 클릭시에 해당 Task 가 완료 되도록 기능 추가 합시다

TaskItem 에 체크박스 클릭 기능 추가

TaskItem-test.js 에 체크박스 아이콘을 클릭하면 이벤트가 호출되는지 테스트 케이스를 추가합니다

//TaskItem-test.js

...생략...

describe('TaskItem interaction', () => {
  beforeEach(() => {
    props = {
      key: '1',
      data: {id: '1', subject: 'Learn React Native', done: false},
      onToggleCheckbox: jest.fn(),
    };
    wrapper = render(<TaskItem {...props} />);
  });
  afterEach(cleanup);

  it('should call toggle method when checkbox is clicked', () => {
    const element = wrapper.getByTestId('checkbox');
    fireEvent(element, 'pressOut');
    expect(props.onToggleCheckbox).toHaveBeenCalledTimes(1);
  });
});

바뀐 점은

data 에 done 이라는 task 의 완료 여부를 판단하는 항목이 추가되었고

onToggleCheckbox 라는 이벤트를 jest mocking 함수로 추가

checkbox 라는 testId 를 이용해서 element 를 가져오고 OnPressOut 이벤트를 발생시키고

props 의 onToggleCheckbox 함수가 1번 실행되는 지 확인합니다

TaskItem 기능 추가

//TaskItem.jsx 수정

...생략...

const TaskItem = ({data, onToggleCheckbox}) => {
  const {subject} = data;

  const handleToggleCheckbox = () => {
    onToggleCheckbox(data);
  };

  return (
    <View style={styles.taskItem}>
      <TouchableOpacity onPressOut={handleToggleCheckbox} testID="checkbox">
        <FeatherIcon name="square" size="small" color="green" />
      </TouchableOpacity>
      <Text style={styles.subject}>{subject}</Text>
      <FeatherIcon name="edit-2" size="medium" color="blue" testId="edit" />
      <FeatherIcon name="delete" size="medium" color="red" testId="delete" />
    </View>
  );
};

...생략...

점점 코드가 길어지고 있어서 앞뒤는 생략 했습니다.

Vector Icon 을 클릭하기 위해서 TouchableOpacity 로 감싸주고 거기에 onPressOut 이벤트를 매핑했습니다 (onPress 도 상관없어요)

ReactNative 공식 문서 보면 onPressIn -> onPressOut -> onPress 순서로 호출이 됩니다

Button 은 안드로이드와 iOS 에서 다르게 보이기 때문에 TouchableOpacity 를 사용한다고 합니다.

여기서는 이미지 아이콘을 클릭할 수 있게 해서 버튼으로 사용하려고 쓰고 있습니다

처음에 Icon 에 testID 를 붙였는데 테스트 코드에서 element 를 가져오면 버튼만 와서 TouchableOpacity 로 testID 를 옮겼습니다

 

코드 짰으면 무조건 테스트

랜더링도 잘되고

테스트도 잘되고

소스를 아무리 마구마구 수정해도

테스트 케이스가 많이 쌓일 수록 제대로 수정되고 있는지 확인할 수 있어서 좋네요

 

TaskList 테스트 케이스에 사용하는 임시 데이터에도 done 항목 추가해주고

onToggleCheckbox 이벤트를 어떻게 TaskList-test.js 에 적용해야 할지 감이 안와서 skip 하고

바로 TaskList.jsx 에 onToggleCheckbox 이벤트를 추가

import React from 'react';
import {ScrollView} from 'react-native';
import TaskItem from './TaskItem';

const TaskList = props => {
  const {data, onToggleCheckbox} = props;
  return (
    <ScrollView>
      {data.map(item => (
        <TaskItem
          key={item.id}
          data={item}
          onToggleCheckbox={onToggleCheckbox}
        />
      ))}
    </ScrollView>
  );
};

export default TaskList;

 

내가 어디까지 수정했는지 모르겠다 싶으면

테스트 실행으로 통과되는 지 확인...

 

이제 HomeScreen-test.jsx 에 체크박스 클릭시 체크박스 이미지가 변경되는 지 테스트 케이스 추가

???? 이미지 변경을 어떻게 잡아내야 하는 거지에 대한 고민 중이네요

텍스트 위주의 테스트 케이스밖에 없어서 혼자 고민 중입니다

//HomeScreen-test.js

...생략...

  it('should toggle status when checkbox is clicked', async () => {
    const elements = wrapper.getAllByTestId('checkbox');
    expect(elements.length).toBe(2);
    fireEvent(elements[0], 'onPressOut');
    
    //여기에 이미지 아이콘을 판별하는 expect를 추가해야 합니다
    //TODO : expect (변경된 이미지 아이콘)
    
    
  });

HomeScreen.jsx 에 handleToggleItem 이벤트 추가

  const handleToggleItem = item => {
    setData(prevData => {
      const newData = [...prevData];
      const index = prevData.indexOf(item);
      newData[index] = {...item, done: !item.done};

      console.log(JSON.stringify(newData));
      return newData;
    });
  };

  return (
    <>
      <Title title="Todo TDD" />
      <TaskInput
        value={newTask}
        onChangeText={handleInputTextChange}
        onSubmitEditing={handleAddTask}
      />
      <TaskList data={data} onToggleCheckbox={handleToggleItem} />
    </>
  );

 

일단 아이콘을 처리 못해서 checkbox 아이콘 클릭시에 done 데이터가 제대로 바뀌는 지 확인하기 위해서 

console.log 를 찍었네요 ㅠㅠ  TDD 의 길은 너무 어렵네요.

 

TODO : 체크박스 아이콘 이미지를 done : true, false 에 따라서 바꿔보자

 

 

 

이건 또 무슨 경고인가 warn : Animated

console.warn
      Animated: `useNativeDriver` is not supported because the native animated module is missing. Falling back to JS-based animation. To resolve this, add `RCTAnimation` module to this app, or remove `useNativeDriver`. Make sure to run `bundle exec pod install` first. Read more about autolinking: https://github.com/react-native-community/cli/blob/master/docs/autolinking.md

해결방법

jest mocking 에서 Animated 안된다 하니 jest 에서 해당 warning 을 무시하게 세팅한다

//jest.setup.js 추가

jest.mock('react-native/Libraries/Animated/NativeAnimatedHelper');

package.json 의 jest 설정에 setupFile 추가

자꾸만 길어져 가는 package.json 과 설정파일들

  "jest": {
    "preset": "react-native",
    "setupFiles": [
      "<rootDir>/jest.setup.js"
    ],
    "setupFilesAfterEnv": [
      "@testing-library/jest-native/extend-expect"
    ],
    "transformIgnorePatterns": [
      "node_modules/(?!(@react-native|react-native|react-native-vector-icons)/).*/"
    ],
    "transform": {
      "^.+\\.jsx$": "babel-jest"
    }
  }

 

반응형