Jest - clipboard test
๐์์ํ๋ฉฐ
๊ฐ์ธ ํ๋ก์ ํธ ํ ์คํธ ์์ฑ์์ ์ค๋ ์๊ฐ ์งํ์ด ์๋์๋ ๋ถ๋ถ์ด ์์๋ค. ์ฌ์ฉ์๊ฐ share-button์ ํด๋ฆญํ๋ฉด clipboard์ ์ํ๋ url์ด ์ ๋๋ก ๋ณต์ฌ๊ฐ ๋๋์ง์ ๋ํ ๋ฌธ์ ์๋๋ฐ ํ ์คํธ ํต๊ณผ ์ ๋ฌธ์ ๊ฐ ์๋๋ผ, ํ ์คํธ ์์ฑ ์์ฒด๊ฐ ์๋ชป๋์๋ค๋ ์๋ฌ๊ฐ ๋ฐ์ํ๋คโฆ ๐ฅ
โ ์ค์ UI ํ๋ฆ
๋ด๊ฐ ํ ์คํธํ๊ณ ์ ํ๋ UI์ ํ๋ฆ์ ๋ค์๊ณผ ๊ฐ๋ค.
- ์ฌ์ฉ์๊ฐ ImageCard๋ฅผ ํด๋ฆญํ๋ฉด zustand๋ฅผ ์ด์ฉํด ํด๋น ImageCard data๋ฅผ Modal ์ปดํฌ๋ํธ๋ก ๋๊ฒจ์ค๋ค.
- Modal Component๊ฐ ๋ ๋๋ง ๋๋ฉฐ share-button์ ์ ๊ณตํ๋ค.
- ์ฌ์ฉ์๊ฐ share-button์ ํด๋ฆญํ๋ฉด, ๋ณต์ฌ๊ฐ ์๋ฃ๋์๋ค๋ alert์ ์ ๊ณตํ๋ค.
- ์ฌ์ฉ์์ clipboard์ ์ด๋ฏธ์ง url์ด ๋ณต์ฌ๋๋ค.
1๋ฒ์ zustand ๊ด๋ จ ํ ์คํธ์ ๊ฒฝ์ฐ ์ด๋ฏธ ์ํ์ฐฉ์ค๋ฅผ ๊ฑฐ์ณ ์๋ฃ๋ ๋ถ๋ถ์ด์ด์ ๋ฌธ์ ๊ฐ ๋์ง ์์์ผ๋, 3๋ฒ 4๋ฒ ํ๋ฆ์์ ๊ณ์ํด์ ์๋ฌ๊ฐ ๋ฐ์ํ๋ค.
โ 1์ฐจ ์๋
1์ฐจ ์๋๋ ๋ค์๊ณผ ๊ฐ๋ค. global ๊ฐ์ฒด๋ฅผ ์ฌ์ฉํ์ฌ navigator๋ฅผ mock ์ฒ๋ฆฌํ๋ ค๊ณ ํ์ผ๋โฆ clipboard๊ฐ readOnly ์ด๊ธฐ ๋๋ฌธ์ ๋ถ๊ฐํ๋ค๋ ๋ฉ์ธ์ง๊ฐ ๋์๋ค.
Cannot assign to โclipboardโ because it is a read-only property.ts(2540)
1
2
3
global.navigator.clipboard = {
writeText: jest.fn().mockResolvedValue(),
};
โ 2์ฐจ ์๋
๋์ ๊ฐ์ ์๋ฌ๋ฅผ ๊ฒช์๊ณ , ํด๊ฒฐํ๋ค๋ ๋ธ๋ก๊ทธ ๊ธ์ ๋ณด๊ณ spyOn์ ๋์ ํด๋ณด๊ณ ์ ํ์ผ๋โฆ ์ด ๋ํ ์๋ฌ๊ฐ ๋ฐ์ํ๋๋ฐ,
1
const writeTextMock = jest.spyOn(navigator.clipboard, 'writeText').mockResolvedValue();
๋ค์๊ณผ ๊ฐ์ด ์์ฑํ์ผ๋
1
expect(writeTextMock).toHaveBeenCalledWith(mockImageData[0].largeImageURL);
toHaveBeenCalledWith
๋ถ๋ถ์์ Number of calls: 0
๋ผ๋ ์๋ฌ๊ฐ ๋ฐ์ํ๋ค. writeTextMock์ด ์ ๋๋ก ํธ์ถ๋์ง ์์๋ค๋ ๋ฌธ์ ์๋๋ฐโฆ
โ 3์ฐจ ์๋
ํน์ ์์์ ๋ฐ์ํ๋ ๋ฌธ์ ๊ฐ, click event ์ฒ๋ฆฌ ์์ ๊ณผ ๊ด๋ จ๋ ๋ฌธ์ ์ผ๊น ํด์ waitFor๋ก ๊ฐ์ธ ์คํํด๋ณด์๊ณ , ์ด๋ฒ์ alert์์ ๋ฌธ์ ๊ฐ ๋ฐ์ํด ์๋์ ๊ฐ์ด ๊ฐ๋จํ๊ฒ mock ์ฒ๋ฆฌ ํด์ฃผ์๋ค.
1
2
3
4
5
6
window.alert = jest.fn();
//waitFor๋ก ๊ฐ์ธ ์๋ํ ์ฝ๋
await waitFor(() => {
expect(writeTextMock).toHaveBeenCalledWith(mockImageData[0].largeImageURL);
});
โ ์ต์ข
์ฌ๋ฌ ์๋ ๋์ ์ต์ข ์ ์ผ๋ก ํ ์คํธ๋ฅผ ์งํํ ์ ์์๋ ์ฝ๋๋ ๋ค์๊ณผ ๊ฐ๋ค.
๋ธ๋ก๊ทธ์์ ๋ณด์๋, ํด๊ฒฐ ๋ฐฉ๋ฒ ์ ์, Jest ํ๊ฒฝ์์๋ ๊ธฐ๋ณธ์ ์ผ๋ก ์กด์ฌํ์ง ์๋ navigator.clipboard๋ฅผ Object.defineProperty๋ฅผ ํตํด ์ ์ํ๊ณ spyOn์ ํตํด ์ด๋ฏธ ์ ์๋ navigator.clipboard.writeText์ ํธ์ถ ํ์ ๋ฑ์ ๊ฐ์ํ๋๋ก ํ ๊ฒ์ด๋ค.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
it("์ฌ์ฉ์๊ฐ ๊ณต์ ๋ฒํผ์ ํด๋ฆญํ๋ฉด ํด๋น URL์ด ๋ณต์ฌ๋๋ค.", async () => {
// alert๋ฅผ mock ์ฒ๋ฆฌ ํ๋ค.
window.alert = jest.fn();
// global.navigator ๊ฐ์ฒด์ clipboard ์์ฑ์ ์ ์ํ๋ค.
Object.defineProperty(global.navigator, "clipboard", {
value: {
writeText: jest.fn(),
},
writable: true,
});
const writeTextMock = jest
.spyOn(navigator.clipboard, "writeText")
.mockResolvedValue();
// mock ๋ฐ์ดํฐ ๋ฐ store ์ค์
mockModalImageIdStore({ modalImage: mockImageData[0] });
// Modal ์ปดํฌ๋ํธ ๋ ๋๋ง
const { getByTestId } = render(<Modal />);
const shareButton = getByTestId("share-button");
// ํด๋ฆญ ์ด๋ฒคํธ๋ฅผ ๋ฐ์์ํจ๋ค.
fireEvent.click(shareButton);
// writeTextMock ํธ์ถ ์ฌ๋ถ๋ฅผ ๊ฒ์ฆํ๋ค.
await waitFor(() => {
expect(writeTextMock).toHaveBeenCalledWith(mockImageData[0].largeImageURL);
});
});
์ต์ข ์ ์ผ๋ก ํ ์คํธ๊ฐ ์๋ํด PASS ๋์๋ค.
๐โโ๏ธ ๋ง๋ฌด๋ฆฌ
๋ธ๋ก๊ทธ ๊ธ์ ๊ฐ๋จํ๊ฒ ์ ์์ง๋ง ์ด clipboard ํ ์คํธ์ ๊ฝค ์ ๋ฅผ ๋จน์๋ค. ๐ฅ ์ด๋ฌ๋ฉด ํ ์คํธ ํ๋๋ฐ ์๊ฐ์ด ๋๋ฌด ๋ง์ด ์์๋๋๊ฑด ์๋๊น ์์ถ๋๊ธฐ๋ ํ๊ณ โฆ
ํ์ง๋ง ์ด๋ป๊ฒ๋ ํด๋๊ธฐ์ ๋จผ์ ์ค์ค๋ก ๋ฐ์๐ค(ใ ใ )๋ฅผ ๋ณด๋ด๋ณธ๋ค.
์ด๋ฒ ํ๋ก์ ํธ์์๋ ์ผ๋ถ๋ฌ ์ค์ ๋ก ํ๋ฉด์ ํ์์๋ ์ปดํฌ๋ํธ๋ฅผ ๋ง๋ค์ด story๋ฅผ ์์ฑํ๊ธฐ๋ ํด์, state๋ฅผ ์ฌ์ฉํด๋ ์๊ด์๋ ๋ก์ง์ zustand๋ฅผ ์จ์ ๋ ๋๋ง ํด ๋ณด๋ ๋ฑ ๋ ์๊ฐํด๋ณด๊ณ ํ ์คํธ๋ฅผ ์์ฑํด์ผ ํ๋ ์ํฉ์ ๋ง๋ค์๋ค.
์ง์ ์์ฑํด๋ณด๋ ๊ณผ์ ์์ ๋ง์ด ๋ฐฐ์ธ ์ ์์๊ณ , ์ง๊ธ๋ ์ฌ์ ํ ๋ฐฐ์ฐ๊ณ ์๋ค๊ณ ์๊ฐํ๋ค.
์ค๋ฌด์ ์ ์ฉํ ์ ์๋ ๊ทธ๋ ๊น์ง ํ๋ด๋ณด์ ํ์ดํ !