이전 작업과 동일하게 TodoItem.js
의 틀만 상태로 만들어 준 다음 작업 시작!
이 컴포넌트는 id
, text
, done
이 들어있는 todo
객체를 props
로 받아와서
텍스트와 button
을 보여주어야 한다.
이전에 만들었던 setup
함수를 사용해서 테스트 코드를 작성해보면,
TodoItem.test.js
import React from 'react';
import TodoItem from './TodoItem';
import { render } from 'react-testing-library';
describe('<TodoItem />', () => {
const sampleTodo = {
id: 1,
text: 'TDD 배우기',
done: false
};
const setup = (props = {}) => {
const initialProps = { todo: sampleTodo };
const utils = render(<TodoItem {...initialProps} {...props} />);
const { getByText } = utils;
const todo = props.todo || initialProps.todo;
const span = getByText(todo.text);
const button = getByText('삭제');
return {
...utils,
span,
button
};
};
it('has span and button', () => {
const { span, button } = setup();
expect(span).toBeTruthy();
expect(button).toBeTruthy();
});
});
이 코드를 테스트에서 통과시키기 위한 코드를 짜자면,
TodoItem.js
import React from 'react';
const TodoItem = ({ todo }) => {
const { id, text, done } = todo;
return (
<li>
<span>{text}</span>
<button>삭제</button>
</li>
);
};
export default TodoItem;
todo
객체에 담겨있는 done
이 true면 삭제선이 보이게, false면 없어지게 만들어보자~
TodoItem.test.js
...
it('shows line-through on span when done is true', () => {
const { span } = setup({ todo: { ...sampleTodo, done: true } });
expect(span).toHaveStyle('text-decoration: line-through;');
});
it('does not show line-through on span when done is false', () => {
const { span } = setup({ todo: { ...sampleTodo, done: false } });
expect(span).not.toHaveStyle('text-decoration: line-through;');
});
not
이라는 키워드를 사용하게 되면 특정조건이 만족하지 않아야 한다는 것을 의미한다.
이번엔 클릭 이벤트까지 코드 작성을 마친 후 테스트 통과를 시켜보겠다.
TodoItme
에서는 텍스트(span
)와 삭제 버튼(button
)을 클릭하면
클릭이벤트가 발생해야 한다.
그러기 위해 done
값을 반전시키는 onToggle함수, 항목삭제를 하는 onRemove함수가 필요하다.
이 두 함수를 props
로 전달받아서 함수들이 호출되게 한다.
여기서 함수들이 호출될 때에는 자신의 id
를 파라미터로 넣어서 호출해야 하는걸 잊지말자!
그럼 테스트케이스를 작성해보자.
TodoItem.test.js
it('calls onToggle', () => {
const onToggle = jest.fn();
const { span } = setup({ onToggle });
fireEvent.click(span);
expect(onToggle).toBeCalledWith(sampleTodo.id);
});
it('calls onRemove', () => {
const onRemove = jest.fn();
const { button } = setup({ onRemove });
fireEvent.click(button);
expect(onRemove).toBeCalledWith(sampleTodo.id);
});
위의 테스트 케이스를 통과시키기 위해 컴포넌트 수정을 해준다.
TodoItem.js
import React from 'react';
const TodoItem = ({ todo, onToggle, onRemove }) => {
const { id, text, done } = todo;
return (
<li>
<span
style={{
textDecoration: done ? 'line-through' : 'none'
}}
onClick={() => onToggle(id)}
>
{text}
</span>
<button onClick={() => onRemove(id)}>삭제</button>
</li>
);
};
export default TodoItem;
여기서 최적화를 위해 useCallback함수를 사용해서 리팩토링을 해줄 수 있다.
const TodoItem = ({ todo, onToggle, onRemove }) => {
const { id, text, done } = todo;
const toggle = useCallback(() => onToggle(id), [id, onToggle]);
const remove = useCallback(() => onRemove(id), [id, onRemove]);
return (
<li>
<span
style={{
textDecoration: done ? 'line-through' : 'none'
}}
onClick={toggle}
>
{text}
</span>
<button onClick={remove}>삭제</button>
</li>
);
};
여기까지의 작업을 마치면 생성된 항목들에 대한 작업까지 마무리!
다음으로 지금까지 만든 TodoItem
을 렌더링해주는 역할인
TodoItemList
작업을 해보자