Как Jest позволяет мутации модулей? - PullRequest
0 голосов
/ 25 января 2019

В этом вопросе, который я задал здесь:

Почему изменение модуля обновляет ссылку при вызове этого модуля из другого модуля, но не при вызове от самого себя?

Я спрашиваю о природе модульной мутации.

Однако, как выясняется, модули ES6 на самом деле не могут быть видоизменены - все их свойства рассматриваются как константы.( См. Этот ответ )

Но каким-то образом - когда Jest тестирует модули - они могут быть видоизменены, и именно так Jest допускает насмешку.

Как это происходит?

Я представляю, что это плагин babel, который работает - переносит модуль в модули CommonJS?Есть ли какая-либо документация по этому поводу?

Есть ли способ просмотра переданного кода?

1 Ответ

0 голосов
/ 28 января 2019

Модули ES6 на самом деле не могут быть видоизменены - все их свойства рассматриваются как константы.

Интересно. Ты прав, даже что-то простое:

import * as lib from "./lib";  // import an ES6 module
const spy = jest.spyOn(lib, 'someFunc');  // spy on someFunc

... технически не должно быть разрешено, поскольку jest.spyOn заменяет метод объекта шпионом , а lib.someFunc должен быть привязкой к someFunc в модуле ES6.


Но каким-то образом - когда Jest тестирует модули - они могут быть видоизменены, и вот как Jest допускает насмешку.

Как это происходит?

Они могут быть изменены только потому, что Jest на самом деле не использует модули ES6.

(думаю, для полноты возможно можно запустить Jest с использованием реальных модулей ES6 с использованием Node экспериментальной поддержки модулей ES6 , но я не пробовал).


Я предполагаю, что это плагин babel, который работает - перенос модуля ... Есть ли какая-либо документация по этому поводу?

"babel-jest автоматически устанавливается при установке Jest и автоматически преобразует файлы, если в вашем проекте есть конфигурация babel. Чтобы избежать этого, вы можете явно сбросить параметр конфигурации transform" .

Таким образом, по умолчанию Jest будет использовать babel-jest, который переносит исходный код с помощью babel (и делает несколько других вещей, таких как подъем вызовов на jest.mock).

Обратите внимание, что Jest также можно настроить с помощью transform, который отображает «регулярные выражения на пути к преобразователям».


Есть ли способ просмотреть перенесенный код?

Да. Преобразования выполняются в jest-runtime здесь и выходные данные сохраняются в кэш здесь .

Самый простой способ взглянуть на переданный код - это просмотреть кэш.

Вы можете сделать это, запустив Jest с параметром --showConfig, который выведет config, используемый при запуске Jest. Расположение кеша можно найти, посмотрев на значение «cacheDirectory».

Затем запустите Jest с параметром --clearCache, чтобы очистить кэш.

Наконец, запустите Jest в обычном режиме, и каталог кэша будет содержать переданный код для вашего проекта.


Пример * +1091 ** * тысяча девяносто-два Последний Jest (v24) будет транслировать этот код:

// lib.js
export const someFunc = () => 1;


// code.js
import { someFunc } from './lib';
export const func = () => someFunc() + 1;


// code.test.js
import { func } from './code';
import * as lib from './lib';

test('func', () => {
  const spy = jest.spyOn(lib, 'someFunc');
  func();
  expect(spy).toHaveBeenCalled();  // SUCCESS
});

... на это:

// lib.js
"use strict";
Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.someFunc = void 0;
const someFunc = () => 1;
exports.someFunc = someFunc;


// code.js
"use strict";
Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.func = void 0;
var _lib = require("./lib");
const func = () => (0, _lib.someFunc)() + 1;
exports.func = func;


// code.test.js
"use strict";
var _code = require("./code");
var lib = _interopRequireWildcard(require("./lib"));
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } }
test('func', () => {
  const spy = jest.spyOn(lib, 'someFunc');
  (0, _code.func)();
  expect(spy).toHaveBeenCalled(); // SUCCESS
});

Линия import * as lib from 'lib'; обрабатывается _interopRequireWildcard, который использует require под капотом.

Каждый вызов require "будет возвращать точно один и тот же объект, если он преобразуется в один и тот же файл" , поэтому code.js и code.test.js получают один и тот же объект из require('./lib') .

someFunc экспортируется как exports.someFunc, что позволяет переназначать его.


Так что да, вы совершенно правы. Шпионаж (или издевательство), подобный этому, работает только потому, что модули ES6 переносятся babel в Node модулями таким образом, что их можно изменять.

...