• Najnowsze pytania
  • Bez odpowiedzi
  • Zadaj pytanie
  • Kategorie
  • Tagi
  • Zdobyte punkty
  • Ekipa ninja
  • IRC
  • FAQ
  • Regulamin
  • Książki warte uwagi

[Jest, React] Zliczanie wywołań funkcji wewnątrz komponentu

Object Storage Arubacloud
+1 głos
177 wizyt
pytanie zadane 17 stycznia 2022 w JavaScript przez Oskar Szkurłat Bywalec (2,780 p.)
edycja 17 stycznia 2022 przez Oskar Szkurłat

Cześć, potrzebuję napisać test dla scenariusza, że renderuje komponent, który ma w sobie wywołanie API (z istancji axiosa za pomocą funkcji getLocationsData) i chciałbym sprawdzić ilu krotnie jest wywołana funkcja po np. po 2 sekundach po wyrenderowaniu. Problemem jednak jest to, że nie wiem co powinienem zmienić, żeby mockowany axios był zliczany z poziomu komponentu, a nie testu. Moja próba:

import { Provider } from 'react-redux'
import { render } from '@testing-library/react'
import '@testing-library/jest-dom/extend-expect'

import LocationsTable from './LocationsTable'

import getLocationsData from '../../../routes/getLocationsData'

import store from '../../../store/index'
import testInput from './testData/input'

jest.mock('../../../routes/getLocationsData')

const LocationsTableComponent = (
  <Provider store={store}>
    <LocationsTable />
  </Provider>
)

test('locations-table-requests-amount', async () => {
  getLocationsData.mockImplementation(() => {
    return new Promise((resolve) => {
      setTimeout(() => {
        resolve(testInput.validLocation)
      }, 500)
    })
  })

  const { getByTestId } = render(LocationsTableComponent)
  const locationsTable = getByTestId('locationsTable')
  expect(locationsTable).toBeInTheDocument()

  await new Promise((resolve) => setTimeout(resolve, 2000))

  expect(getLocationsData).toHaveBeenCalled()
  expect(getLocationsData).toHaveBeenCalledTimes(1)
})

Wynikiem testu jest:
 

expect(jest.fn()).toHaveBeenCalled()

Expected number of calls: >= 1      
Received number of calls:    0 

Druga sprawa jeszcze, jak TypeScript można poinformować o tym, że to już jest mock, a nie obiekt danego typu, żeby nie zwracał błędu, że nie ma w nim metody mockImplementation()?

Z góry dziękuję :)

1 odpowiedź

+1 głos
odpowiedź 17 stycznia 2022 przez ScriptyChris Mędrzec (190,190 p.)

chciałbym sprawdzić ilu krotnie jest wywołana funkcja po np. po 2 sekundach po wyrenderowaniu.

A dlaczego przyjąłeś założenie czasowe? Co sprawia, że po jakimś czasie funkcja getLocationsData jest wołana z komponentu? Jest tam faktycznie jakiś setTimeout, następuje reakcja na response z API, obserwujesz zmiany w DOM czy czekasz na reakcje użytkownika - jak do tego mają się te 2 sekundy? Bo może lepiej jest w teście poczekać konkretnie na to, co powoduje wywołanie funkcji, zamiast czekać określoną ilość czasu (co bywa kruche w asynchroniczności)?


Jeśli dobrze rozumiem, to funkcja getLocationsData korzysta z axiosa i sama jest używana przez komponent LocationsTable. Dlaczego najpierw (pozornie*) importujesz jej oryginał przez import getLocationsData from '../../../routes/getLocationsData' , a potem ją mockujesz jest.mock('../../../routes/getLocationsData') i korzystasz z mocka zamiast z oryginału?

*Mock tworzony przez odwołanie do jest.mock jest hoistowany (w przeciwieństwie do jest.doMock), więc jest.mock wykonuje się przed importem (z pozoru) oryginału funkcji getLocationsData - pewnie dlatego TypeScript sądzi, że to nie jest mock, bo zauważył import oryginału. Jeśli chcesz jednocześnie czytelnie zamockować moduł (dla komponentów, które korzystają z niego jako zależności) i działać na nim w teście, to zapisz w ten sposób:

const getLocationsData = jest
  .mock('../../../routes/getLocationsData') // <-- mockuj dla zależności
  .mockRequire('../../../routes/getLocationsData'); // <-- pobierz mocka do zmiennej

Wtedy, za jednym zamachem, komponent LocationsTable będzie korzystać z mocka (zakładając, że sam go importuje), Ty będziesz mieć mocka do użycia w teście i TypeScript powinien zauważyć, że korzystasz z mocka.

nie wiem co powinienem zmienić, żeby mockowany axios był zliczany z poziomu komponentu, a nie testu

Dlaczego potrzebujesz żeby mockowany axios był zliczany z poziomu komponentu a nie testu? I w ogóle, gdzie mockujesz axiosa?

komentarz 21 stycznia 2022 przez Oskar Szkurłat Bywalec (2,780 p.)

A dlaczego przyjąłeś założenie czasowe? Co sprawia, że po jakimś czasie funkcja getLocationsData jest wołana z komponentu?

Używam biblioteki DevExtreme, która to w rękach nieznajomionej z nią osoby jest na tyle "niebezpieczna", że wywołanie np. filtru (co nie jest wg. mnie intuicyjne) jest w stanie wysłać drugi request o dane do tabeli z API. Mam też obsługę profili, więc w momencie wczytania się tabelki lecą filtry i mnóstwo innych eventów. Profile oczywiście też wymagają czasu, bo to też kolejne API, więc już mamy tu "setTimeout". Dlatego właśnie przyjąłem założenie "wyrenderuj mi tabelkę, poczekaj kilka sekund i upewnij się, że ilość łącznych requestów o dane tabelki jest równa jeden". Bo taki scenariusz oczekuję, że jest poprawny. Gdyby np. po wczytaniu filtrów (np. 1 sec po wczytaniu tabelki) doszło do drugiego requesta - a już się zdarzyło, to znaczy że źle użyto devexa :). Trochę taki test nie funkcjonalności, a bardziej czy coś się zrypało w czarnej skrzynce.

Dlaczego potrzebujesz żeby mockowany axios był zliczany z poziomu komponentu a nie testu? I w ogóle, gdzie mockujesz axiosa?

Nie mockuje axiosa, tylko funkcję getLocationsData, która czyta z istancji axiosa (axios.create()) i wysyła request pod konkretny url. Jakbym próbował mockować axiosa, to bym miał prędzej zliczanie każego API requesta, niezależnie od tego który to jest, prawda? Mi potrzeba konkretnie tego location.list, bo w tle zachodzi jeszcze kilka innych API calli

import axios from './axios'
import { location } from './apiPaths.json'

const getLocationsData = async () => {
  try {
    const url = location.list
    const response = await axios.get(url)

    return response.data
  } catch (error) {
    console.log(error)
  }

  return []
}

export default getLocationsData

--------------

Na ten moment chwilowo muszę wstrzymać prace nad tym problemem ze względu na priorytety w projekcie ;( dziękuję za sugestie, mam nadzieję, że jeszcze wrócę niedługo do tego tematu, żeby poszerzyć swoją wiedzę z JEST w końcu. W razie pytań będę pisał ponownie na tym temacie, dlatego proszę o niezamykanie.

Podobne pytania

0 głosów
1 odpowiedź 152 wizyt
+2 głosów
0 odpowiedzi 200 wizyt
pytanie zadane 3 grudnia 2021 w JavaScript przez lzrd Nowicjusz (160 p.)
+1 głos
1 odpowiedź 172 wizyt

92,549 zapytań

141,391 odpowiedzi

319,512 komentarzy

61,933 pasjonatów

Motyw:

Akcja Pajacyk

Pajacyk od wielu lat dożywia dzieci. Pomóż klikając w zielony brzuszek na stronie. Dziękujemy! ♡

Oto polecana książka warta uwagi.
Pełną listę książek znajdziesz tutaj.

Akademia Sekuraka

Kolejna edycja największej imprezy hakerskiej w Polsce, czyli Mega Sekurak Hacking Party odbędzie się już 20 maja 2024r. Z tej okazji mamy dla Was kod: pasjamshp - jeżeli wpiszecie go w koszyku, to wówczas otrzymacie 40% zniżki na bilet w wersji standard!

Więcej informacji na temat imprezy znajdziecie tutaj. Dziękujemy ekipie Sekuraka za taką fajną zniżkę dla wszystkich Pasjonatów!

Akademia Sekuraka

Niedawno wystartował dodruk tej świetnej, rozchwytywanej książki (około 940 stron). Mamy dla Was kod: pasja (wpiszcie go w koszyku), dzięki któremu otrzymujemy 10% zniżki - dziękujemy zaprzyjaźnionej ekipie Sekuraka za taki bonus dla Pasjonatów! Książka to pierwszy tom z serii o ITsec, który łagodnie wprowadzi w świat bezpieczeństwa IT każdą osobę - warto, polecamy!

...