묻고 답해요
141만명의 커뮤니티!! 함께 토론해봐요.
인프런 TOP Writers
-
해결됨실무에 바로 적용하는 프런트엔드 테스트 - 1부. 테스트 기초: 단위・통합 테스트
4.4 getBy~, queryBy~ 질문입니다
마지막, 삭제 버튼 테스트Q. 삭제 버튼을 누르면 TableRow가 사라지니까 queryByText('text').not.toBeInTheDocument()를 사용해서 유무를 확인 하셨는데getByText('text').not.toBeInTheDocument()를 사용해서 해당 텍스트가 있는 요소가 없으면 에러가 나타나도록 유도해서 테스트 검증할 수도 있지 않나요?? 가능은 한건지, 권장이 되지 않는건지 질문 드립니다
-
미해결실무에 바로 적용하는 프런트엔드 테스트 - 1부. 테스트 기초: 단위・통합 테스트
이 작업 영역에서 아직 발견된 테스트가 없습니다.
다른 분들도 겪은 문제인데 0.2.42 버전으로 다운그레이드 하려고해도 지원하지 않는 버전이라고 나오네요. npm run test 는 잘 실행되는거 같습니다. 혹시 해당 이슈 해결방법 알 수 있을까요 .. 아니면 그냥 npm run test 로 진행해도 상관없을까요
-
미해결실무에 바로 적용하는 프런트엔드 테스트 - 1부. 테스트 기초: 단위・통합 테스트
vitest 실행시 테스트 실행에서 출력을 기록하지 않았습니다
결과가 저렇게나오는데 yarn test 실행하면 로그가 정상적으로 출력됩니다 이유가뭘까요?
-
미해결실무에 바로 적용하는 프런트엔드 테스트 - 1부. 테스트 기초: 단위・통합 테스트
질문 있습니다.
안녕하세요. 강의 내용 중 정확히 이해하지 못하고 있는 것이 있어서 질문 드립니다.강의 중 로그인 버튼 같이 간단한 동작만 있는 컴포넌트는 그 자체를 테스트 하는 것이 아니라, 통합 테스트로 상태에 따른 동작을 검증하는 것이 더 효율적이고 정확한 테스트가 된다. 라고 하셨습니다.또한 이런 컴포넌트는 스토리북과 같은 도구를 사용하여 스타일이나 레이아우싱 틀어지지 않는지 확인하는 것이 더 중요하다. 라고 말씀 하셨습니다.이 뜻은 통합 테스트를 스토리북과 같은 도구와 함께 하라는 것이 아니라, 통합 테스트로 상태에 따른 동작을 검증하고, 그 안에 있는 각각의 컴포넌트들은 스토리북 안에서 스타일과 레이아웃이 틀어지지 않는지 확인하라는 것일까요?아니면 스토리북을 통해 네비게이션 같은 컴포넌트를 만들고 그 단위로 통합 테스트를 하란 말씀이실까요??궁금합니다. 강의 너무 잘 듣고 있습니다. 정말 감사합니다!
-
해결됨실무에 바로 적용하는 프런트엔드 테스트 - 1부. 테스트 기초: 단위・통합 테스트
2강 storybook & vitest
안녕하세요. 2.1강에서 storybook, vitest 명령어를 사용했을 때, 에러가 나타나서 질문 드립니다 Settingbranch: unit-test-examplevitest 플러그인: v0.3.0 (현재 이하로는 변경이 안되는 것 같습니다)node: 19.9.0 eslint.json - `"prettier:prettier": "off"` 추가prettierrc - `"endOfLine": "auto"` 추가 storybook 사용 시(npm run storybook) Web의 경우 새로고침을 하면 정상 페이지로 동작하지만 에러 로그는 그대로 입니다vitestimport { screen } from '@testing-library/react'; import React from 'react'; import TextField from '@/components/TextField'; import render from '@/utils/test/render'; it('className Prop으로 설정한 css class가 적용된다.', async () => { // Arrange - 테스트를 위한 환경 만들기 // -> className을 지닌 컴포넌트 렌더링 await render(<TextField className="my-class" />); // Act - 테스트할 동작 발생 // -> 렌더링에 대한 검증이기 때문에 이 단계는 생략 // -> 클릭이나 메서드 호출, prop 변경 등등에 대한 작업이 여기에 해당 합니다. // Assert - 올바른 동작이 실행되었는지 검증 // -> 렌덩링 이후 DOM에 해당 class가 존재하는지 검증 // TextField는 placeholder 요소가 있습니다. // vitest의 expect 함수를 사용하여 기대 결과를 검증 expect( screen .getByPlaceholderText('텍스트를 입력해 주세요.') .toHaveClass('my-class'), ); });
-
해결됨실무에 바로 적용하는 프런트엔드 테스트 - 1부. 테스트 기초: 단위・통합 테스트
mockServiceWorker.js 파일이 프로젝트내에 포함되어 있어야 하나요?
실무에 적용하려고 하니 msw 에서 많이 막히네요 ㅠ 그래서 올려주신 깃헙 프로젝트를 샅샅히 훑어보고 있는데 mockServiceWorker.js 이 파일과 package.json에"msw": { "workerDirectory": "public" } 요런 부분이 있더라고요. 요것들의 역할이 뭔지 알수 있을까요? msw 사이트에 가서 Getting started 를 가봐도 안나와 있는것 같아서 궁금합니다!
-
해결됨실무에 바로 적용하는 프런트엔드 테스트 - 1부. 테스트 기초: 단위・통합 테스트
toHaveClass 테스트코드 관련하여 질문 남깁니다!
안녕하세요 현재 섹션2 관련하여 강의 듣고 있는 수강생입니다. 다름이 아니오라, 현재 강의에선 순수 css를 통해 클래스명 테스트 코드를 작성해주셨는데요..! 만약에 css 모듈이나, vanilla-extract 같이 클래스명이 동적으로 생성되는 경우에는 어떤식으로 테스트 코드를 작성해야할까요 ? 예전에 혼자서 vanilla-extract 관련해서 테스트 코드를 작성해보려고 했던적이 있는데, 단순히 toHaveClass로만 하려고하니까, 클래스명이 동적으로 결정이 되어서 에러가 계속 발생하더라구요..! (예를 들어서 클래스명이 header라면, headerhnfgbvds341 이런식) 강의 너무 잘 듣고 있습니다! 읽어주셔서 감사합니다. import React from "react"; import { render, screen } from "@testing-library/react"; import TestComponent from "@/components/TestComponent"; import { testContainer } from "@/components/testComponent.css"; describe("Example Component Test", () => { test("클래스명에 대한 테스트", () => { render(<TestComponent />); const exampleElement = screen.getByText("TestComponent"); expect(exampleElement).toHaveClass(testContainer); }); }); ```
-
해결됨실무에 바로 적용하는 프런트엔드 테스트 - 1부. 테스트 기초: 단위・통합 테스트
4.3 강의 자료가 이상해요
4.3 강의 자료 pdf가 아닌 key 파일이 다운받아집니다.
-
해결됨실무에 바로 적용하는 프런트엔드 테스트 - 1부. 테스트 기초: 단위・통합 테스트
shopping-mall-integration-test 브랜치에 이상한 점 발견
4.6 RTL 비동기 유틸 함수를 통한 노출 테스트 작성 강의 수강중 문제 발견shopping-mall-integration-testshopping-mall-integration-test-answer 두 브랜치 사이에 테스트 코드가 아닌 부분이 차이가 있는 것을 발견했습니다. ProductCard.jsx 파일강의 내용과는 상관 없는 부분이 소스코드가 달라서에러가 발생하는 것을 확인했습니다. 이마저도 테스트 코드를 통해 검증하긴 했지만처음에는 제가 테스트 코드가 익숙치 않아 잘못하고 있는 건가 싶었네요. 해당 부분 확인 한번 부탁드릴게요.
-
해결됨실무에 바로 적용하는 프런트엔드 테스트 - 1부. 테스트 기초: 단위・통합 테스트
show more 버튼이 노출되지 않는 테스트 케이스에서 limit 오동작 문제
안녕하세요, 좋은 강의 잘 듣고 있습니다.보여줄 상품 리스트가 없는 경우 show more 버튼이 노출되지 않는다. 테스트 케이스에서 limit 를 20 이하로 입력해도 테스트 실패로 잡히지 않는 문제가 있는 것 같아 보이는데요..!원인을 찾아보려 조금 테스트하다 보니.. render -> screen.findAllByTestId 를 하면서 api가 두 번 호출되는 것 같습니다. 그 과정에서 offset이 limit 만큼 증가해 호출되고 있어요. (즉, 두번째 페이지까지 렌더링했을 때를 기준으로 테스트가 돌아가는 것 같습니다)it('보여줄 상품 리스트가 없는 경우 show more 버튼이 노출되지 않는다.', async () => { await render(<ProductList limit={2} />); // offset : 0 await screen.findAllByTestId('product-card'); // offset : 2 });offset 이 useInfiniteQuery에서 리턴해주는 pageParams로 인해 만들어지는 것 같아서 pageParams가 원인인 것 같긴 한데.. 해결방법을 모르겠네요.혹시 이 부분 수정이 어떻게 되면 좋을지 확인 부탁드립니다..! +)테스트하다가 발견한 건데요, apiRoutes.products 를 mocking하는 handler에서 lastPage로 리턴하는 조건이 잘못된 건 아닌가 해서요..!ctx.json({ products, lastPage: products.length < limit });이면 테스트 코드에서 limit을 10으로 준다고 해도, 두 번째 페이지에서 lastPage가 false인 것 같습니다.ctx.json({ products, lastPage: data.products.length <= offset + limit });으로 수정되어야 하지 않을까 조심스럽게 제안드려 봅니다..!
-
해결됨실무에 바로 적용하는 프런트엔드 테스트 - 1부. 테스트 기초: 단위・통합 테스트
상태 검증과 행위 검증에 대해서 질문이 있어 남기게 되었습니다!
현재 하고 있는 프로젝트에 테스트 코드를 연습하고 있는데 상태 기반 검증은 보통 "custom hook"과 같이 비즈니스 로직에 하고, 행위 검증은 컴포넌트의 이벤트 처리와 같은 상황에 하고 있는데 이렇게 진행하는게 맞는 건지에 대해서 궁금해서 질문을 남기게 되었습니다!vitest에서는 stub과 mock과 같은 테스트 더블을 완벽하게 구별짓지 않는다고 생각하고 있는데 맞을까요? 추가적으로 공부를 해보았을 때 상태 기반 검증은 stub, fake 행위 기반 검증은 mock, spy로 하는 것이라고 나누었는데 이것이 맞는 내용인가요?
-
해결됨실무에 바로 적용하는 프런트엔드 테스트 - 1부. 테스트 기초: 단위・통합 테스트
navigate 관련 테스트에서 질문있습니다!
버튼을 눌렀을 때, navigate 하는 경우를 테스트할 때는 클릭 시 함수가 호출되었는지에 대한 테스트만 하면 되는 건가요??혹시, 특정 경로로 잘 이동되었는지에 대한 테스트를 하는 방법이 있는지 여부와 해당 테스트가 존재한다면 통합테스트인건지 단위 테스트인건지 궁금합니다! 그리고, 그런 테스트가 존재한다면 어떻게 assert할 수 있는지도 알고 싶습니다!
-
해결됨실무에 바로 적용하는 프런트엔드 테스트 - 1부. 테스트 기초: 단위・통합 테스트
테스트 실행 중 에러가 납니다.
vitest 를 통해 실행하면 계속 위와 같은 에러가 나는데, 어떤 이유일까요? 커멘드라인을 이용해서 npm run test 를 입력하면 그 때는 테스트가 잘 이뤄집니다.
-
해결됨실무에 바로 적용하는 프런트엔드 테스트 - 1부. 테스트 기초: 단위・통합 테스트
안녕하세요. 훅 테스트 질문이 있습니다!
예제에서 말씀해주신것처럼,result.current.setState() 를 호출해서 act() 를 통해 업데이트 된 상태를 검증하는 방법을 말씀해주셨는데요. 훅 내부 이펙트에서 상태를 업데이트하는 로직을 검증하려면 어떤 식으로 검증해야할까요? export const useDarkMode = (defaultTheme = THEME["LIGHT"]) => { const [theme, setTheme] = useState(defaultTheme); const changeTheme = (type: keyof typeof THEME) => { setTheme(THEME[type]); }; useLayoutEffect(() => { const mediaQueryList = window.matchMedia("(prefers-color-scheme: dark)"); const changeListener = ({ matches }: MediaQueryListEvent) => { setTheme(THEME[matches ? "DARK" : "LIGHT"]); }; mediaQueryList.addEventListener("change", changeListener); return () => { mediaQueryList.removeEventListener("change", changeListener); }; }, []); return { theme, changeTheme, }; }; 위 useDarkMode() 훅 내부 useLayoutEffect() 에서window.matchMedia 의 change 이벤트를 감지하면, setTheme() 하도록 설계되어 있는데요. window.matchMedia 함수의 matches 결과를 true 로 모킹하고,window.matchMedia.dispatchEvent('change') 를 일으켜 검증을 시도해보았는데요.생각처럼 검증이 되지 않는 것 같습니다.ㅠ 혹시 이렇게 검증을 시도하는 것이 맞는지. 검증하는게 맞는지. 여쭤봐도 될까요? 감사합니다.
-
해결됨실무에 바로 적용하는 프런트엔드 테스트 - 1부. 테스트 기초: 단위・통합 테스트
prop이나 state 값을 검증하지 않는다는 의미가 궁금합니다!
const textField = screen.getByPlaceholderText('텍스트를 입력해 주세요.'); expect(textField).toHaveClass('my-class');이부분에서 className이란 내부 props, state 값을 검증하는 게 아닌가 싶어서 질문을 드렸습니다. 물론, className에 따라 변경되는 DOM을 파악한다는 의미로도 해석이 될 수는 있을 것 같긴 하지만 더 정확한 문맥을 알고 싶어서 질문드렸습니다..!
-
해결됨실무에 바로 적용하는 프런트엔드 테스트 - 1부. 테스트 기초: 단위・통합 테스트
4.3. 강의와 깃헙 소스코드가 다른 부분
안녕하세요. 강의 잘 듣고 있습니다.다름이 아니라 강의와 깃헙 소스코드가 달라서 문의드려요. mocks/zustand.js의 코드인데요.const { create: actualCreate } = await vi.importActual('zustand'); import { act } from '@testing-library/react'; // 앱에 선언된 모든 스토어에 대해 재설정 함수를 저장 const storeResetFns = new Set(); // 스토어를 생성할 때 초기 상태를 가져와 리셋 함수를 생성하고 set에 추가합니다. export const create = createState => { const store = actualCreate(createState); const initialState = store.getState(); storeResetFns.add(() => store.setState(initialState, true)); return store; }; // 테스트가 구동되기 전 모든 스토어를 리셋합니다. beforeEach(() => { // 👈 이 부분 act(() => storeResetFns.forEach(resetFn => resetFn())); });깃헙 소스코드는 위와 같이 beforeEach를 사용하지만 강의에서는 afterEach로 설명해주시고 있습니다.주석도 마찬가지로 다릅니다.무엇이 맞는 걸까요?
-
미해결실무에 바로 적용하는 프런트엔드 테스트 - 1부. 테스트 기초: 단위・통합 테스트
react-cookie로 쿠키값 테스트코드로 가져오는 방법
describe('로그인이 성공했을 경우', () => { it('"아이디 저장" 체크박스를 체크했을 시 쿠키에 itall_admin 객체의 id key로 유저가 입력한 아이디를 저장한다.', async () => { const { user } = await render(<LoginPage />); const rememberIdCheckbox = screen.getByLabelText('아이디 저장'); const idInput = screen.getByPlaceholderText('아이디(이메일)'); const submitButton = screen.getByRole('button', { name: '로그인' }); const setCookies = vi.fn(); setCookies.mockReturnValueOnce('center.test@itall.com'); const cookieValue = setCookies(); await user.click(rememberIdCheckbox); await user.type(idInput, 'center.test@itall.com'); await user.click(submitButton); expect(cookieValue).toBe('center.test@itall.com'); }) })Forms.spec.ts파일에서 js-cookie 라이브러리 모킹해서 하시는 예시를 보고 react-cookie 사용한 프로젝트의 테스트코드를 작성중인데요. 혹시 라이브러리에서 제공하는 함수를 이렇게 setCookies라는 함수를 임의로 모킹해서 return값을 지정해서 저렇게 처리해도 되는걸까요..? 테스트는 당연히 통과되었습니다..
-
미해결실무에 바로 적용하는 프런트엔드 테스트 - 1부. 테스트 기초: 단위・통합 테스트
4.4 통합테스트에서 정적 데이터로 테스트하는 대신 role 값을 미리 설정해 직접 돔에 접근하는 방식은 어떤가요?
안녕하세요우선 좋은 강의를 제작해주셔서 감사드립니다공식 문서만으로 테스트 코드 작성을 공부했다면 훨씬 시간이 많이 들었을 텐데한글로 설명을 듣고 문서를 보니 좀 더 빠르게 이해할 수 있는 것 같습니다. // answer 브랜치 코드 it('특정 아이템의 수량이 변경되었을 때 값이 재계산되어 올바르게 업데이트 된다', async () => { const { user } = await render(<ProductInfoTable />); const [firstItem] = screen.getAllByRole('row'); const input = within(firstItem).getByRole('textbox'); await user.clear(input); await user.type(input, '5'); // 2427 + 809 * 2 = 4045 expect(screen.getByText('$4,045.00')).toBeInTheDocument(); });궁금한 점은 현재 제공해주신 정답 코드에서는모킹 데이터의 결과 포맷을 알기 때문에 '$4,045.00' 이라는 텍스트 값이 dom에 마운트 되어야 테스트를 통과 시키는 방식인데요 it('특정 아이템의 수량이 변경되었을 때 값이 재계산되어 올바르게 업데이트 된다', async () => { const { user } = await render(<ProductInfoTable />); const [firstItem] = screen.getAllByRole('row'); const input = within(firstItem).getByRole('textbox'); // role은 price를 담는 div에 미리 추가했다고 가정 const price = Number(within(firstItem).getByRole('price').textContent); const value = 5; await user.clear(input); await user.type(input, value.toString()); const pricedResult = within(firstItem).getByRole('price').textContent; // 2427 + 809 * 2 = 4045 expect(priceResult.includes((value*price).toLocaleString())).toBe(true); });제가 작성한 방식은엘리먼트마다 role을 미리 지정해 둔 다음에테스트할 때마다 element들의 값에 접근해서 테스트를 진행하는 방식입니다.제가 생각했을 때에는 이 방식을 사용하면 element마다 role을 직접 설정해주어 element의 용도를 파악하기 더 쉽고 getAllBy... 메소드로 가져온 요소들에 대해 순회하여 테스트할 때 테스트 결과 값을 동적으로 생성하기 때문에 더 유연하지 않을까 라는 생각이 들었습니다. 궁금한 점은 제가 작성한 방식을 현업에서도 사용하는지잘 사용되지 않는 방식이라면 어떤 이유에서 잘 사용되지 않는지가 궁금합니다
-
해결됨실무에 바로 적용하는 프런트엔드 테스트 - 1부. 테스트 기초: 단위・통합 테스트
3.4. 타이머 테스트의 마지막 테스트 질문입니다.
it('연이어 호출해도 마지막 호출 기준으로 지정된 타이머 시간이 지난 경우에만 함수가 호출된다.', () => { const spy = vi.fn(); const debouncedFn = debounce(spy, 300); // 최초 호출 debouncedFn(); // 최초 호출 후 0.2초 후 호출 vi.advanceTimersByTime(200); debouncedFn(); // 두번째 호출 후 0.1초 후 호출 vi.advanceTimersByTime(100); debouncedFn(); // 세번째 호출 후 0.2초 후 호출 vi.advanceTimersByTime(200); debouncedFn(); // 👈 4번째 호출 // 네번째 호출 후 0.3초 후 호출 // 최초 호출 후에 함수 호출 간격이 0.3초 이상 -> 다섯번째 호출이 유일 vi.advanceTimersByTime(300); debouncedFn(); // 👈 5번째 호출 // 다섯번을 호출했지만 실제 spy함수는 단 한 번만 호출 expect(spy).toHaveBeenCalledTimes(1); }); 안녕하세요. 새해 복 많이 받으세요 :)다름이 아니라 위 코드에서 4번째 호출 후 바로 뒤, vi.advanceTimersByTime(300);로 인해 0.3초가 흘렀고 이로 인해 expect(spy).toHaveBeenCalledTimes(1); 이 맞다고 나온 것이 아닌지 궁금해서 질문드립니다.강의에서는 4번째 호출이 아닌 5번째 호출로 인해 호출된다고 하셨는데(최초 호출 후에 함수 호출 간격이 0.3초 이상인 경우는 다섯번째 호출이 유일하다고 하신 것 같아요) 5번째 호출 후에는 0.3초가 흘렀다는 가정 (vi.advanceTimersByTime(300);)이 없고 제가 혼자 테스트 해보며 5번째 호출 후에 vi.advanceTimersByTime(300);를 넣어 0.3초가 흘렀다고 가정을 하니 호출이 2번 되었다고 떠서요.제가 debounce를 헷갈려 했는데 제가 아직 잘 이해하지 못한 부분이 있는 건지 아니면 실제로 잘못 설명된 부분이 있는 건지 모르겠어서 질문 드립니다. 감사합니다.
-
해결됨실무에 바로 적용하는 프런트엔드 테스트 - 1부. 테스트 기초: 단위・통합 테스트
mocking과 spy함수가 헷갈립니다.
mocking과 spy함수가 조금 헷갈립니다. 아래와 같이 정리하면 될까요?- spy 함수 : 빈 함수인데, vitest에서 이 함수를 감지하고 있고 함수가 call 되었는지, 인자는 무엇이었는지 검증하는 가짜 함수.- mocking : 종속성이 있는 라이브러리를 복사해두고, 그 중 사용해야 할 함수나 기능을 spy 함수로 대체하여 call 했는지 검사할 수 있는 프로세스.그러면 mocking 자체는 spy 함수 없이 사용하는 것은 의미가 없다고 보면 될까요?