Как я могу заставить шпионскую функцию jest хранить внутри себя аргументы, с которыми функция была вызвана путем глубокоJavascript

Форум по Javascript
Ответить
Гость
 Как я могу заставить шпионскую функцию jest хранить внутри себя аргументы, с которыми функция была вызвана путем глубоко

Сообщение Гость »


Я пытаюсь протестировать класс, который сочетает в себе выборку, кэширование, геокодирование и возврат списка мест. Тестируемый код выглядит примерно так (это не полностью работающий исполняемый код, но достаточный, чтобы проиллюстрировать проблему):

Место интерфейса { имя: строка; адрес: строка; долгота: число | нулевой; широта: число | нулевой; } класс Места { findAll(country: string): Place[] { пусть места = this.placesCache.get(страна) если (места === ноль) места = this.placesExternalApiClient.fetchAll (страна); // ТОЧКА останова № 1 (перед вызовом store()) this.placesCache.store(страна, места); // ТОЧКА останова №2 (после вызова store()) } for (константное место мест) { // ТОЧКА останова № 3 (перед вызовом geocode()) const geocodedAddress = this.geocoder.geocode(place.address); место.долгота = геокодированныйадрес.долгота; место.широта = геокодированныйадрес.широта; } вернуть места; } } класс PlacesExternalApiClient { fetchAll(country: string): Place[] { // делает запрос к какому-то внешнему API-серверу, анализирует результаты и возвращает их } } класс PlacesCache { магазин (страна: строка, места: Место []) { // сохраняет страну и места в базе данных с отношением } get(country: string): Place[] | нулевой { // если страна есть в базе данных, возвращает все соответствующие места (возможно []), // если страны нет в базе данных, возвращается ноль } } интерфейс GeocodedAddress { адрес: строка; долгота: число; широта: число; } класс Геокодер { геокод (адрес: строка): GeocodedAddress { // делает запрос к какому-либо сервису геокодирования, например Google Geocoder, // и возвращает лучший результат. } } А это тест (вроде как иллюстрирующий проблему):

mockedPlaces = [ {имя: «место № 1», адрес: «Атлантида», долгота: ноль, широта: ноль }, { name: «место № 2», адрес: «Марсианская база № 3», долгота: ноль, широта: ноль }, ] mockedPlacesExternalApiClient = { fetchAll: jest.fn().mockImplementation(() => структурированныйClone(mockedPlaces)) } mockedGeocodedAddress = { адрес: «здесь не имеет значения», долгота: 1337, широта: 7331, } mockedGeocoder = { геокод: jest.fn().mockImplementation(() => StructuredClone(mockedGeocodedAddress)) } описать('Места#findAll()', () => { it('при двухкратном вызове метод должен вызвать функцию кэша#store() один раз', ​​() => { const storeMethod = jest.spyOn(placesCache, 'store'); Places.findAll('abc'); Places.findAll('abc'); ожидать (storeMethod).toHaveBeenCalledTimes (1); ожидать(storeMethod).toHaveBeenNthCalledWith( 1, 'абв', mockedPlaces, // ОШИБКА: ожидаемые места имеют lng и широту null, null // но полученные места имеют длину и широту 1337, 7331 ); }) }) Во время отладки я обнаружил, что во время первой итерации places.findAll() (когда происходит промах в кэше):
[*]в BREAKPOINT №1: places (var) имеет null в качестве координат [*]в BREAKPOINT №2: places (var) по-прежнему имеет null в качестве координат [*]placesCache.store (который является шуточным объектом Spied) имеет .mock.calls[0][1], который представляет собой массив где есть два места, оба с null для lng и широты, как и ожидалось [*]при первом попадании в BREAKPOINT №3: ничего не изменилось в .mock.calls и ничего не изменилось в places (var) при втором попадании в BREAKPOINT №3: первое место в обоих массивах: places (var) и массиве .mock.calls[0][1 ] изменили свои координаты на 1337, 7331!
из-за этого позже, когда я спрашиваю шутку, с каким аргументом был вызван storeMethod, он ошибочно думает, что он был вызван с широтой и долготой 1337, 7331, а не с null, null.

Это не работает так, как я ожидал, потому что spyOn просто присваивает/переносит аргументы, которые он получил в .mock.calls[], он не выполняет глубокое копирование, и поэтому, когда аргумент — это объект, который позже мутирует, это изменение также влияет на то, что шпионский объект записал в качестве аргумента вызова.

Как заставить spyOn глубоко клонировать получаемые аргументы? или «Как я могу заставить этот тест работать по-другому, не меняя реализацию бизнес-логики (первый фрагмент)?»

Я хочу проверить, что store() был вызван с долготой и широтой, установленными в null. Теперь мой тест дал ложноотрицательный результат.
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «Javascript»