<p>Фабрика jest.mock не работает внутри теста</p>

Созданный на 12 янв. 2017  ·  38Комментарии  ·  Источник: facebook/jest

Вы хотите запросить функцию или сообщить об ошибке ?
Ошибка

Каково текущее поведение?

Кажется, что способ создания макета с фабрикой не работает внутри test или it . Он работает только тогда, когда макет определен на корневом уровне файла.

Вот мой пример макета:

jest.mock('services/feature', () => ({
    isEnabled: () => true
}));

Какое ожидаемое поведение?

Мокинг файла внутри теста должен работать.

Укажите точную конфигурацию Jest и укажите свой Jest, узел, версию yarn / npm и операционную систему.

Jest 18.0.0, Узел 7.4, macOS

Confirmed Discussion

Самый полезный комментарий

Чтобы изменить возвращаемое значение макета между тестами, вы можете сделать что-то вроде этого:

jest.mock('whatever');

// Get the mock function
const whatever = require('whatever');

test('test 1', () => {
  whatever.mockImplementation(() => 'hello');
});

test('test 2', () => {
  whatever.mockImplementation(() => 'world');
});

Все 38 Комментарий

jest.mock вызовы автоматически поднимаются в начало файла с помощью преобразования babel-jest. Вы можете опустить это поведение с помощью jest.doMock . Вы пробовали это?

То же самое с doMock .

В документации я могу читать

Примечание. При использовании babel-jest вызовы имитации автоматически переносятся в верхнюю часть блока кода. Используйте doMock если вы хотите явно избежать такого поведения.

Но ... Тест - это блок кода, верно? Так что в моем случае я не ожидаю увидеть никаких различий.

Вот полный тест

it('renders with the enabled feature', () => {
  jest.mock('services/feature', () => ({
      isEnabled: () => true
  }));

  const component = renderer.create(
      <MyComponent />
  );

  const tree = component.toJSON();
  expect(tree).toMatchSnapshot();
});

Не могли бы вы предоставить копию этого в репозитории GH?

Конечно. Вот он: https://github.com/tleunen/jest-issue-2582

Оба теста отображают «Отключено», хотя в одном из них вместо этого используется макет для визуализации «Включено».
Смотрите это:
https://github.com/tleunen/jest-issue-2582/blob/master/src/MyComponent.js
https://github.com/tleunen/jest-issue-2582/blob/master/src/__tests__/MyComponent.spec.js

Спасибо.

Какие-нибудь советы @thymikee @cpojer по этой проблеме?
У меня есть несколько тестов в одном файле, и я хотел бы получить разные фиктивные ответы для каждого из них.

Рад, что обнаружил эту проблему, я ломал себе голову, почему jest.mock() не работает в моей describe области. Переместил его наверх (под моим импортом в тестовом файле), и он работает.

Для меня это также относится к jest.mock() без фабрики, используя папку __mocks__ содержащую фиктивный файл.

--редактировать

Очевидно, необходимо поднять оператор jest.mock() до операторов импорта. Однако @tleunen , это, вероятно, означает, что невозможно

На мой взгляд, было бы более явным, если бы jest.mock() всегда помещалось вне блоков describe и it . Но это должно быть четко указано в документации.

Итак, jest.mock поднимается в область видимости функции, поэтому он не будет работать с require s (и определенно не с import s, которые поднимаются в область видимости модуля), если вы вызовите его внутри функции, отличной от describe (которая обрабатывается Жасмин специально).
Ваш вызов jest.mock будет поднят наверх этой самой функции (а не модуля), поэтому он не будет работать так, как вы ожидаете.

Обычно мы советуем настраивать разные макеты в beforeEach и afterEach если вы хотите, чтобы они были разными в разных тестовых примерах.

@cpojer может подробно рассказать об этом, и если мы хотим поднять вызовы в верхние области.

Это связано с тем, что вам требуются ваши модули при инициализации модуля (с использованием импорта). jest.mock вызывается позже. Способ решить эту проблему:

beforeEach(() => { // or the specific test
  jest.mock('MyModule', () => …);
  const MyModule = require('MyModule');
  …
});

и т.п.

Если бы вы сделали это в beforeEach, мне неясно, как бы вы дифференцировали тесты (так как бы вы давали разные макеты для каждого теста?)

Помещение внутрь самих тестов конечно работает.

Можете ли вы передать имитируемый модуль внутри тестируемого компонента?

Что делать, если я не импортирую / не требую файл, который хочу имитировать (например, зависимость другого файла, который я импортирую), но я хочу, чтобы он ограничивался блоком описания / it? Или даже если я хочу по-разному издеваться над тестом beforeEach / beforeAll? Возможны ли такие случаи?

// A.js depends on B.js
import A from './A';

describe('myTest', () => {

    describe('myFirstScope', () => {
        beforeAll(() => {
            jest.mock('./B', () => ({
                myFirstMethod: jest.fn(),
            }));
        });

        // tests here
    });

    describe('mySecondScope', () => {
        beforeAll(() => {
            jest.mock('./B', () => ({
                mySecondMethod: jest.fn(),
            }));
        });

        // tests here
    });
});

В этом случае вам нужно потребовать A после издевательства B. (Не используя import , но require ).

требуя A после издевательства B

не сработало для меня, все еще видел, как оригинальный макет B переходит в результат

@GoldAnna Вы нашли способ решения этой проблемы?

@alayor у меня нет

Сколько раз я сталкивался с этой проблемой, теперь я убежден, что либо неправильно тестирую, не использую jest как предполагали авторы, либо комбинацию того и другого. Дополнительным доказательством является то, что почти ни один из связанных с макетами примеров в документации jest кажется мне примерами из реального мира ... вероятно, так оно и есть, и мой подход, вероятно, неверен.

Тем не менее, у меня работает следующее, и все следующие тесты проходят. Обратите внимание: чтобы вернуться к исходной версии ModuleB , мне нужно вызвать jest.resetModules() и jest.unmock('./moduleB') ... порядок их не имеет значения.

// Module A
const ModuleB = require('./moduleB');
const ModuleA = function() {
  this.title = new ModuleB().title;
  return this;
};
module.exports = ModuleA;

// Module B
const ModuleB = function() {
  this.title = 'Module B - Original'
  return this;
};
module.exports = ModuleB;

// Tests
describe('Jest a few tests', () => {
  it('should do something', () => {
    jest.resetModules();
    jest.mock('./moduleB', () => function() {
      this.title = 'Module B - Mock 1'
      return this;
    });
    const ModuleA = require('./moduleA');
    const moduleA = new ModuleA();
    expect(moduleA.title).toEqual('Module B - Mock 1');
  });

  it('should do something else', () => {
    jest.resetModules();
    jest.mock('./moduleB', () => function() {
      this.title = 'Module B - Mock 2'
      return this;
    });
    const ModuleA = require('./moduleA');
    const moduleA = new ModuleA();
    expect(moduleA.title).toEqual('Module B - Mock 2');
  });

  it('should do something original', () => {
    jest.resetModules();
    jest.unmock('./moduleB');
    const ModuleA = require('./moduleA');
    const moduleA = new ModuleA();
    expect(moduleA.title).toEqual('Module B - Original');
  });
});

Привет,

У меня здесь ничего не работает.
Может кто-нибудь объяснить, почему в нем невозможно использовать jest.mock?

Спасибо

Я думаю, что это все еще проблема, которую, возможно, следует повторно открыть в качестве запроса функции. Я хочу писать тесты изолированно. Выгрузка вещей в папку mocks или переналадка с помощью beforeEach обычно заканчивается ящиком для мусора, содержащим фиктивные / странные экземпляры данных, которые таскаются в каждом тесте. Я хочу написать небольшой макет и пройти небольшой тест.

У нас нет реального способа изолировать отдельные тесты (учитывая test.concurrent ). Если бы мы приняли API, похожий на tap или ava, это было бы возможно, но я не думаю, что это возможно с ограничениями, налагаемыми текущим глобальным API. Супер рад, что ошибся!

Здесь возникла борьба с несколькими методами для рефакторинга теста, ранее написанного на mocha и proxyquire, в конечном итоге разделение теста на разные файлы для разных макетов.

@gcox Пробовали ли вы mockImplementation . Вот как мы решили эту проблему в наших тестовых наборах.

@antgonzales Я думаю, что это сложно сделать, если импорт модулей поднимается до вершины модуля. Изменение ссылок на ранее импортированный модуль кажется нетривиальным или невозможным в node.

@jkomusin Определенно. Это просто не то, о чем просила ОП, ИМО. Я полагаю, они спрашивали, в частности: «Как заставить require('whatever') возвращать другой макет из нескольких смежных тестов, а не один и тот же объект?».

Чтобы изменить возвращаемое значение макета между тестами, вы можете сделать что-то вроде этого:

jest.mock('whatever');

// Get the mock function
const whatever = require('whatever');

test('test 1', () => {
  whatever.mockImplementation(() => 'hello');
});

test('test 2', () => {
  whatever.mockImplementation(() => 'world');
});

Ответ @gcox сработал для меня, но @SimenB тоже работал, и это намного проще!

@rafaeleyng, но @SimenB не работает, если то, что вы экспортируете из модуля, не является функцией ...

Пример @gcox содержит большую часть информации, которую я искал в документации в течение последних нескольких часов. Я предлагаю включить его в официальную документацию, которая в любом случае очень скудна на данный момент.

@schumannd прав. Сделайте официальную документацию более ясной и четкой.

PR всегда приветствуют улучшение документации 🙂

Пример модуля на основе
https://github.com/facebook/jest/issues/2582#issuecomment -378677440 ❤️

Привет, нашел это через связанную проблему, и я понял это:

jest.mock('child_process')
const childProcess = require('child_process')

describe('foo', () => {
  test('bar', () => {
    childProcess.execSync.mockImplentation(jest.fn().mockReturnValueOnce('wibble'))
    // code that itself requires child_process
    expect(childProcess.execSync.mock.results[0]).toEqual('wibble')
  })

  test('baz', () => {
    childProcess.execSync.mockImplentation(jest.fn().mockReturnValueOnce('wobble'))
    // code that itself requires child_process
    expect(childProcess.execSync.mock.results[0]).toEqual('wobble')
  })
})

Если вам нужно смоделировать несколько функций / методов в требуемом коде, вам нужно настроить их отдельно, а не использовать фабрику, когда вы делаете это глобально.

Если вам, как мне, нужна всего одна функция, вы можете все упростить:

jest.mock('child_process')
const { execSync } = require('child_process')

describe('foo', () => {
  test('bar', () => {
    execSync.mockImplentation(jest.fn().mockReturnValueOnce('wibble'))
    // code that itself requires child_process
    expect(execSync.mock.results[0]).toEqual('wibble')
  })

  test('baz', () => {
    execSync.mockImplentation(jest.fn().mockReturnValueOnce('wobble'))
    // code that itself requires child_process
    expect(execSync.mock.results[0]).toEqual('wobble')
  })
})

как насчет имитации не функциональных зависимостей, таких как файлы json или сопоставленные константы. Пожалуйста, проясните в документации, как с этим справиться ...

@gcox Ваше решение - единственное, которое я нашел, когда тестируемый импортированный модуль импортирует другой модуль, для которого у меня был ручной макет в папке __mocks__ .

Например, тестовый файл вызывает jest.mock('./ModuleA') , у которого есть макет в __mocks__/ModuleA.js . Но ModuleA не тестируется, ModuleB, для которого требуется ModuleA, тестируется. Без вашего решения ModuleB получил бы фактическую реализацию ModuleA, а не макет.

Это кажется действительно странным поведением из-за шутки. Я ожидал, что когда я вызываю jest.mock в модуле, любой тестируемый модуль, который зависит от макетированного модуля, будет использовать макет. Кажется странным, что это не так, или я делаю это совершенно неправильно?

@SimenB В вашем примере вам

@SimenB сработал для меня ваше более простое решение. Спасибо! Wish мог найти его раньше, прежде чем я тратил время на множество разных способов

В моем случае я издевался и импортировал неправильный путь к модулю, который хотел заглушить. У меня был другой регистр в пути к файлу, поэтому мой редактор думал, что это нормально, но просто терпел неудачу.

Было

const { stubMethod } = require("./path/to/File");
jest.mock("./path/to/File");

Изменено на (изменить регистр File на file ):

const { stubMethod } = require("./path/to/file");
jest.mock("./path/to/file");

Надеюсь, это поможет кому-то другому.

Жасмин spyOn() не имеет такой проблемы _внутренние_ тесты. Якобы Jest по умолчанию использует жасмин? Почему не работает внутри теста?

Пример жасмина:

spyOn(require('moduleB'), 'functionA').and.callFake(() => true);

Что я не совсем понимаю, когда дело доходит до имитации модулей, так это того, что он всегда описывается так, как будто вы хотите использовать модуль в первую очередь в своих тестовых функциях. Например, если вы посмотрите пример кода для функции doMock в документации:

test('moduleName 2', () => {
  jest.doMock('../moduleName', () => {
    return {
      __esModule: true,
      default: 'default2',
      foo: 'foo2',
    };
  });
  return import('../moduleName').then(moduleName => {
    expect(moduleName.default).toEqual('default2');
    expect(moduleName.foo).toEqual('foo2');
  });
});

В этом примере у вас есть доступ к недавно смоделированной версии "moduleName" внутри тестовой функции. Для меня гораздо важнее, чтобы мой "не тестовый" код также имел доступ к макетированной версии модуля. Например, класс, который я использую в своем тесте, который импортировал "moduleName" , по-прежнему будет использовать исходную версию, а не имитируемую.

Кажется, связано: https://github.com/facebook/jest/issues/3236

Совет - если у вас есть модуль, который экспортирует примитивное значение, например:

`` auth.js
экспорт const isUserAdmin = getSetting ('admin');

And you want to use a mock value instead, in the test, then a simple require and assignment seems to do the trick:

```deleteUser.js
const auth = require('../auth');

describe('deleteUser', () => {
  it('can delete if admin', () => {
    auth.isUserAdmin = true;

    // call method that depends on isUserAdmin value
    // and assert on the result
  });
});

Итак, в основном, если вы хотите имитировать объект, который косвенно используется кодом, который вы тестируете, функция jest.doMock() не будет работать:

import myModuleToTest from './myModuleTotest'

describe('Given my module', () => {
  it('property1 will work as expect', () => {
    // Testing parts of the module that don't need to be mocked
  })

  it('property2 will work as expected', () => {
    jest.doMock('./myOtherModule', () => {
      return {
        __esModule: true,
        default: 'default2',
        foo: 'foo2',
      };
    });

    import('./myOtherModule').then(myOtherModule => {
      // I'm not interested on the mocked module myOtherModule but on the module that makes use of it
      myModuleToTest.doSomethingToSomeProperty(); // At this point myOtherModule's original module and not its mocked version will be used by myModuleToTest
      expect(myModuleToTest.someProperty).toBe('thisWillFail'); // The test won't pass because the mocked version wasn't used
    });
  });
});

Начиная с Jest 26, нет возможности более одного раза издеваться над модулем, экспортирующим Object который используется косвенно (я имею в виду издевательство над чем-то другим, кроме Function поскольку mockFn.mockImplementation(fn) для насмешливого Objects ). Это правильно или я что-то упускаю? Единственное решение - иметь более одного тестового файла для тестирования одного и того же модуля.

Вам нужно импортировать myModuleToTest после вашего макета, потому что, если он импортирует
раньше макет не имеет смысла. Итак, не используйте импорт .... сверху или
внутри обратного вызова, потому что он все равно поднят.

7 июля 2020 г., 22:24 Антонио Редондо [email protected]
написал:

В общем, если вы хотите имитировать объект, который косвенно используется
код, который вы тестируете, функция jest.doMock () не будет работать:

импортировать myModuleToTest из './myModuleTotest'
it ('будет работать', () => {
jest.doMock ('./ myOtherModule', () => {
возвращение {
__esModule: правда,
по умолчанию: 'default2',
foo: 'foo2',
};
});

return import ('../ myOtherModule'). then (myOtherModule => {
// Меня не интересует фиктивный модуль myOtherModule, а модуль, который его использует
myModuleToTest.doSomethingToSomeProperty (); // На этом этапе myModuleToTest будет использовать исходный модуль myOtherModule, а не его фиктивную версию
ожидать (myModuleToTest.someProperty) .toBe ('thisWillFail'); // Тест не пройдет, потому что имитируемая версия не использовалась
});});

Начиная с Jest 26, нет возможности имитировать модуль, экспортирующий объект (I
означает нечто иное, чем функция), который используется косвенно. Это
правильно или я что-то упускаю? Единственное решение тогда - иметь больше
чем один тестовый файл для тестирования одного и того же модуля.

-
Вы получили это, потому что прокомментировали.
Ответьте на это письмо напрямую, просмотрите его на GitHub
https://github.com/facebook/jest/issues/2582#issuecomment-655110424 или
отказаться от подписки
https://github.com/notifications/unsubscribe-auth/AAJR3UW6HARW44ZLKAUB7PLR2N77NANCNFSM4C4I7QSQ
.

Была ли эта страница полезной?
0 / 5 - 0 рейтинги