INSTACOOLIKE 테스팅 코드 TIL

YEONGHUN KO·2022년 6월 6일
1

JAVASCRIPT TESTING

목록 보기
3/11
post-thumbnail
post-custom-banner

fireEvent에 await 붙이기

사진에서 처럼 await has no effect 라고 되어있으면서 await를 없애면 event가 먹히지 않는다... 아무것도 입력되지 않은것으로 나온다. await를 입력하자...(진짜 이것때문에 1시간이상을 헤맸다... jest 일해라...)

toHaveBeenCalledWith

Use .toHaveBeenCalledWith to ensure that a mock function was called with specific arguments.

it('test',()=>{
    const signInWithEmailAndPassword: jest.fn(()=> (Promise.resolve("successfully signed in")))

    // signInWithEmailAndPassword has been called with arguments passed as value.
    fireEvent.change(getByPlaceholderText('Email address'), {target: { value: 'karl@gmail.com' }});
    fireEvent.change(getByPlaceholderText('Password'), { target: { value: 'password' } });
    fireEvent.submit(getByTestId('login'));
    expect(signInWithEmailAndPassword).toHaveBeenCalledWith("karl@gmail.com","password")
})

버그

toHaveBeenCalled

// signup.test.js
expect(doc).toHaveBeenCalled(); // 0

가 분명 1이상이 되어야 하는데 0이 나왔다. 즉 함수가 호출 되지 않았다는 거다.
이유를 알아보니 Signup.js에서 doc앞에 에러가 나서 그런것이었다.

// Signup.js
console.log('before newUsers');
const newUsers = {
  userId: createdUserResult.user.uid,
  username: username.toLowerCase(),
  fullName,
  emailAddress: emailAddress.toLocaleLowerCase(),
  following: ['2'],
  followers: [],
  dateCreated: Date.now(),
  photoURL,
};
console.log('after newUsers');
const userRef = doc(db, 'users', username);

알아보니 before은 찍히는 데 after는 안찍힌다. 알아보니 createdUserResult에서 에러가 났던것.
createdUserResult를 mock할때 obj가 아닌 string을 리턴했기 떄문.. {user:{uid:'uid'}} 를 리턴해주는 걸로 mock을 하니 잘 작동함.

mocking useNavigate or only one function from module

아무리 이것저것 시도해봐도 useNavigate를 mocking하는 과정에서 에러가 났다...

import * ROUTES from '../../constants/route'

// first try
jest.mock('react-router-dom', () => {
    // Require the original module to not be mocked...
    return {
        __esModule: true,
        ...jest.requireActual('react-router-dom');,
        // add your noops here
        useNavigate: jest.fn(() => 'bar')
    };
});

// second try
 jest.mock('react-router-dom', () => ({
    ...jest.requireActual('react-router-dom');,
    useNavigate: jest.fn(() => jest.fn),
}))


// third try
var mockUseNavigate = jest.fn()
 jest.mock('react-router-dom', () => ({
    ...jest.requireActual('react-router-dom');,
    useNavigate: mockUseNavigate,
}))

// fourth try
import {useNavigate} from 'react-router-dom'

jest.mock('react-router-dom')
it('sign up',() =>{
    useNavigate.mockImplementation(()=>jest.fn())
})

expect(useNavigate).toHaveBeenCalledWith(ROUTES.DASHBOARD)

// fifth try
const mockUseNavigate = jest.fn();

jest.mock('react-router-dom', () => ({
  ...jest.requireActual('react-router-dom'),
  useNavigate: jest.fn(() => mockUseNavigate),
}));

위의 것을 전부 시도해보았지만 0 arugmnets passed 라거나 TypeError: (0 , _reactRouterDom.useNavigate) is not a function 라거나 Number of calls: 0 라는 에러가 떴다.

그러다가.. stackoverflow에서 어떤 글의 제목을 보았다. react jest can't mock useNavigate

과연...!!

찾았다 해답을!

const mockUseNavigate = jest.fn();

jest.mock('react-router-dom', () => ({
  ...jest.requireActual('react-router-dom'),
  useNavigate: () => mockUseNavigate,
}));
// Signup.js
const navigate = useNavigate();
navigate(ROUTES.DASHBOARD);

그렇다 useNavigate 자체는 일반 함수가 되어야한다. 근데 일반함수가 아닌 jest.fn() 이더라도 정상작동해야하는게 상식적으로 맞는것같은데... 그냥 호출하는 함수는 일반함수가 되어야하나보다..

그리고 검색할때 jest mock useNavigate 요렇게만이라도 검색했으면 찾을 수 있었을텐데.. 검색을 좀더 정확하고 심플하게 하도록 연습해보자!

또한 requireActual api로 어떤 파일안에 export된 수많은 함수중 특정 함수 하나만 mocking 할 수 있다.(ex> useUserStore만 ./src/store에서 모킹)

아래 글도 참고.

mock only one function from module

관련자료

follow 버튼을 찾을 수 없다

그러나 waitfor안에서는 찾을 수 있다. 그러나 waitfor안에서 조차 찾을 수 없는 것이 viewmore와 suggested profile follow 버튼이었다.

rendering은 잘 된다. fixture도 잘 넘어온다... 왜그럴까??... karl은 어떻게 했을까??

  • 해결!!!!
    viewMore 버튼을 찾을 수 없는 이유는 단순했다.. 댓글이 3개 이하여서... 그래서 댓글을 몇개 추가했더니 버튼이 생겼다.

suggested profile 같은 경우는 아무래도 로딩 시간이 길어서 못찾은거 같다. 로딩되는 도중에 찾으려고 하니 못찾았던게 아닌가 생각이 든다.

그래서 suggested profile을 waitFor api안에다가 넣고 fireEvent에다가 await를 추가하니 suggested profile follow버튼을 테스팅 할 수 있었다.

await waitFor(async () => {
  const follow = getByTestId('suggested-profile-utH4EadD3gBUbQkdG6Da');
  fireEvent.click(follow);
});

그런데 그뒤에 expect(follow).tobeFalsy()를 하려고하니 follow를 찾을 수 없다는 에러가 떴다... click하면 당연히 follow가 없어지니 falsy가 되어버리는 줄 알았는데 아예 없으니깐 에러가 나나보다.

그래서 queryselector를 이용하기로 했다.

await waitFor(async () => {
  const follow = getByTestId('suggested-profile-utH4EadD3gBUbQkdG6Da');
  fireEvent.click(follow);
  const followEle = container.querySelector(
    '[data-testid="suggested-profile-utH4EadD3gBUbQkdG6Da"]'
  );
  expect(followEle).toBeFalsy();
});

그랬더니 또 follow를 찾을 수 가 없다고 한다. 그래서 또다시 기다렸다가 다시 찾아야 되나 싶어서 waitFor를 한번더 사용하였다.

await waitFor(() => {
  const follow = getByTestId('suggested-profile-utH4EadD3gBUbQkdG6Da');
  fireEvent.click(follow);
  const followEle = container.querySelector(
    '[data-testid="suggested-profile-utH4EadD3gBUbQkdG6Da"]'
  );
  expect(followEle).toBeFalsy();
});

await waitFor(() => {
  const follow = container.querySelector(
    '[data-testid="suggested-profile-utH4EadD3gBUbQkdG6Da"]'
  );
  expect(follow).toBeFalsy();
});

이러니깐 잘 된다!

테스팅 속도가 현저하게 느려진 이유

dashboard를 테스팅하던 도중 갑자기 속도가 엄청나게 느려지는게 아닌가?... 이것 저것 시도해본 결과 역시나 mock이랑 render를 act안에다가 실행해서 그랬다... act바깥으로 빼내자!

profile
'과연 이게 최선일까?' 끊임없이 생각하기
post-custom-banner

1개의 댓글

comment-user-thumbnail
2023년 10월 28일

감사합니다. 저도 await 추가하니 해결되네요 !

답글 달기