• 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

Aruba Cloud VPS - 50% taniej przez 3 miesiące!
+1 głos
204 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ź 197 wizyt
+2 głosów
0 odpowiedzi 276 wizyt
pytanie zadane 3 grudnia 2021 w JavaScript przez lzrd Nowicjusz (160 p.)
+1 głos
1 odpowiedź 214 wizyt

93,177 zapytań

142,190 odpowiedzi

321,983 komentarzy

62,509 pasjonatów

Advent of Code 2024

Top 15 użytkowników

  1. 1637p. - dia-Chann
  2. 1614p. - Łukasz Piwowar
  3. 1599p. - CC PL
  4. 1597p. - Łukasz Eckert
  5. 1572p. - Tomasz Bielak
  6. 1545p. - Michal Drewniak
  7. 1537p. - Łukasz Siedlecki
  8. 1531p. - rucin93
  9. 1509p. - rafalszastok
  10. 1506p. - Marcin Putra
  11. 1487p. - Adrian Wieprzkowicz
  12. 1447p. - Mikbac
  13. 1356p. - ssynowiec
  14. 1289p. - Anonim 3619784
  15. 1169p. - Grzegorz Aleksander Klementowski
Szczegóły i pełne wyniki

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

Wprowadzenie do ITsec, tom 1 Wprowadzenie do ITsec, tom 2

Można już zamawiać dwa tomy książek o ITsec pt. "Wprowadzenie do bezpieczeństwa IT" - mamy dla Was kod: pasja (użyjcie go w koszyku), dzięki któremu uzyskamy aż 15% zniżki! Dziękujemy ekipie Sekuraka za fajny rabat dla naszej Społeczności!

...