diff --git a/src/posts/63-test-async-with-jest.mdx b/src/posts/63-test-async-with-jest.mdx index fd47b15..720695b 100644 --- a/src/posts/63-test-async-with-jest.mdx +++ b/src/posts/63-test-async-with-jest.mdx @@ -4,6 +4,7 @@ title: jest로 비동기 함수 테스트하기 description: 함수를 모킹하고, fake timer를 활용해 비동기 함수의 실행 시점을 제어해봅니다. tags: jest,test,글또 date: 2023-03-26 +seriesId: web-frontend-test --- diff --git a/src/posts/75-storybook-interaction-tests.mdx b/src/posts/75-storybook-interaction-tests.mdx index 9d9ce05..c75edbe 100644 --- a/src/posts/75-storybook-interaction-tests.mdx +++ b/src/posts/75-storybook-interaction-tests.mdx @@ -4,6 +4,7 @@ title: Storybook Interaction Test를 활용한 바텀시트 시각적 테스트 description: 시각적 요소가 포함된 기능은 어떻게 테스트할 수 있을까요? 어떤 기능을 어떻게 테스트해 볼 수 있을지, 바텀시트 높이 조절 테스트 사례를 통해 소개합니다. tags: Test date: 2023-12-24 +seriesId: web-frontend-test --- ## 👀 문제 상황 @@ -26,45 +27,54 @@ date: 2023-12-24 먼저, 바텀시트 컴포넌트에서 제공하는 기능을 리스팅했다. - - - - - + + + + + - - + + - - + + - - + + - + - - + + - + - + - + - + - - + + - +
ContextItContextIt
heightType = hug일 경우바텀시트의 높이는 바텀시트 컨텐츠 높이가 된다heightType = hug일 경우바텀시트의 높이는 바텀시트 컨텐츠 높이가 된다
핸들을 50px 아래로 당기면 바텀시트를 닫을 수 있다핸들을 50px 아래로 당기면 바텀시트를 닫을 수 있다
heightType = fixed일 경우, snapPoints = [100px, 200px, 500px, 600px]바텀시트의 높이는 snapPoints 중 가장 작은 값이 된다 (100px) + heightType = fixed일 경우, snapPoints = [100px, 200px, 500px, 600px] + 바텀시트의 높이는 snapPoints 중 가장 작은 값이 된다 (100px)
핸들을 50px 위로 당기면 한 단계 큰 snapPoint로 높이가 조정된다 (200px) + 핸들을 50px 위로 당기면 한 단계 큰 snapPoint로 높이가 조정된다 (200px) +
핸들을 600px 위치로 당기면 바텀시트 높이가 600px이 된다핸들을 600px 위치로 당기면 바텀시트 높이가 600px이 된다
핸들을 50px 아래로 당기면 한 단계 낮은 snapPoint로 높이가 조정된다 (500px) + 핸들을 50px 아래로 당기면 한 단계 낮은 snapPoint로 높이가 조정된다 + (500px) +
핸들을 가장 작은 snapPoints보다 50px 낮게 내리면 바텀시트가 닫힌다 + 핸들을 가장 작은 snapPoints보다 50px 낮게 내리면 바텀시트가 닫힌다 +
heightType = fullPage일 경우,바텀시트의 높이는 스크린의 높이와 같다heightType = fullPage일 경우,바텀시트의 높이는 스크린의 높이와 같다
### 🤔 React Testing Library로 할 수 없을까? @@ -77,40 +87,53 @@ date: 2023-12-24 바텀시트 컴포넌트를 테스트하기 위해, RTL을 사용해 테스트를 작성해 두고, 기능을 개발하려고 했다. 처음 작성한 테스트는 아래와 같다. ```tsx -describe('bottom sheet', () => { - describe('heightType: fixed일 때', () => { - const TestBottomSheet = ({ snapPoints }: { snapPoints: BottomSheetHeightValue[] }) => { +describe("bottom sheet", () => { + describe("heightType: fixed일 때", () => { + const TestBottomSheet = ({ + snapPoints, + }: { + snapPoints: BottomSheetHeightValue[]; + }) => { const [open, setOpen] = useState(true); return ( - - header + + header ); }; - const snapPoints: BottomSheetHeightValue[] = ['100px', '200px', '500px', '600px']; + const snapPoints: BottomSheetHeightValue[] = [ + "100px", + "200px", + "500px", + "600px", + ]; - test('snap points 중 작은 값으로 열려야 한다', async () => { + test("snap points 중 작은 값으로 열려야 한다", async () => { const wrapper = createWrapper(); render(, { wrapper }); - const bottomSheet = await screen.findByRole('dialog'); + const bottomSheet = await screen.findByRole("dialog"); expect(bottomSheet.getBoundingClientRect().height).toBeCloseTo(200); }); - test('헤더를 잡고 마우스를 놓은 위치가 300이면 바텀시트의 높이가 200px이 된다', async () => { + test("헤더를 잡고 마우스를 놓은 위치가 300이면 바텀시트의 높이가 200px이 된다", async () => { const wrapper = createWrapper(); render(, { wrapper }); - const header = screen.getByTestId('header'); + const header = screen.getByTestId("header"); fireEvent.pointerDown(header); fireEvent.pointerMove(header, { clientY: header.clientTop + 200 - 300 }); fireEvent.pointerUp(header); - const bottomSheet = await screen.findByRole('dialog'); + const bottomSheet = await screen.findByRole("dialog"); expect(bottomSheet.getBoundingClientRect().height).toBeCloseTo(200); }); }); @@ -145,7 +168,7 @@ describe('bottom sheet', () => { 이를 우회하기 위해 `getComputedStyle` 을 사용할 수도 있다. ```tsx -expect(getComputedStyle(bottomSheet).getPropertyValue('height')).toBe('200px'); +expect(getComputedStyle(bottomSheet).getPropertyValue("height")).toBe("200px"); ``` 하지만, getComputedStyle은 계산된 CSS를 반환하는 것이지 실제로 렌더링되는 값과는 차이가 발생할 수도 있다. 뷰포트 상에 렌더링된 정확한 값을 검증하려면 `getBoundingClientRect`를 사용하는 게 더 낫다. @@ -188,13 +211,13 @@ npm install @storybook/testing-library @storybook/jest @storybook/addon-interact ```tsx // .storybook/main.ts -import type { StorybookConfig } from '@storybook/your-framework'; +import type { StorybookConfig } from "@storybook/your-framework"; const config: StorybookConfig = { // ... addons: [ // Other Storybook addons - '@storybook/addon-interactions', // 👈 여기 애드온 추가 + "@storybook/addon-interactions", // 👈 여기 애드온 추가 ], }; @@ -216,17 +239,21 @@ export const HugBotomSheet: StoryObj = { render: () => { const [open, setOpen] = useState(true); return ( - setOpen(false)} heightType={'hug'}> - + setOpen(false)} + heightType={"hug"} + > + ); }, play: async ({ canvasElement, step }) => { - await step('바텀시트 높이는 바텀시트 컨텐츠의 높이이다', async () => { + await step("바텀시트 높이는 바텀시트 컨텐츠의 높이이다", async () => { // 여기에 테스트 작성하기 }); - await step('핸들을 50px 아래로 당기면 바텀시트가 닫힌다', async () => { + await step("핸들을 50px 아래로 당기면 바텀시트가 닫힌다", async () => { // 여기에 테스트 작성하기 }); }, @@ -317,4 +344,4 @@ test-storybook 스토리북 인터랙션 테스트에서 조금 아쉬운 점은, 테스트 작성 시 `testing-library`를 그대로 사용하는 것이 아니라 스토리북에서 래핑해둔 것을 사용해야 하는데, fireEvent가 정상적으로 동작하지 않는 등 RTL과는 약간씩 다르게 동작하는 부분이 있었다. 이런 경우 테스트를 어떻게 작성해야 할 지에 대한 레퍼런스가 아직 많이 부족한 것 같아 테스트 작성에 약간의 어려움이 있었다. -그렇지만 시각적 요소를 간편하게 테스트 할 수 있다는 점에서는 정말 좋았다. 앞으로도 RTL만으로는 테스트할 수 없는 부분이 있거나, 컴포넌트의 인터랙션을 스토리 상에 남겨둬야 하는 경우가 있다면 자주 사용하게 될 것 같다. \ No newline at end of file +그렇지만 시각적 요소를 간편하게 테스트 할 수 있다는 점에서는 정말 좋았다. 앞으로도 RTL만으로는 테스트할 수 없는 부분이 있거나, 컴포넌트의 인터랙션을 스토리 상에 남겨둬야 하는 경우가 있다면 자주 사용하게 될 것 같다.