简体   繁体   English

如何使用异步 function 作为 jest.mock 的第二个参数?

[英]How to use an async function as the second parameter to jest.mock?

I need to use jest.mock together with async , await and import() so that I can use a function from another file inside the mocked module.我需要将jest.mockasyncawaitimport()一起使用,以便我可以使用模拟模块内另一个文件中的 function 。 Otherwise I must copy and paste a few hundreds of slocs or over 1000 slocs, or probably it is not even possible.否则我必须复制和粘贴几百个 sloc 或超过 1000 个 sloc,或者甚至不可能。

An example一个例子

This does work well:这确实很好用:

jest.mock('./myLin.jsx', () => {
  return {
    abc: 967,
  }
});

Everywhere I use abc later it has 967 as its value, which is different than the original one.我以后在任何地方使用 abc 时,它的值都是 967,这与原始值不同。

This does not work:这不起作用:

jest.mock('./myLin.jsx', async () => {
  return {
    abc: 967,
  }
});

abc seems to not be available. abc似乎不可用。

Actual issue实际问题

I need async to be able to do this:我需要async才能做到这一点:

jest.mock('~/config', async () => {
  const { blockTagDeserializer } = await import(
    '../editor/deserialize' // or 'volto-slate/editor/deserialize'
  );

  // … here return an object which contains a call to
  // blockTagDeserializer declared above; if I can't do this
  // I cannot use blockTagDeserializer since it is outside of
  // the scope of this function
}

Actual results实际结果

I get errors like:我收到如下错误

TypeError: Cannot destructure property 'slate' of '((cov_1viq84mfum.s[13]++), _config.settings)' as it is undefined.

where _config , I think, is the ~/config module object and slate is a property that should be available on _config.settings .我认为 _config 是~/config模块_configslate是应该在_config.settings上可用的属性。

Expected results预期成绩

No error, blockTagDeserializer works in the mocked module and the unit test is passed.没有错误, blockTagDeserializer在模拟模块中工作,单元测试通过。

The unit test code单元测试代码

The code below is a newer not-working code based on this file on GitHub .下面的代码是基于GitHub 上的此文件的更新的无效代码。

import React from 'react';
import renderer from 'react-test-renderer';
import WysiwygWidget from './WysiwygWidget';
import configureStore from 'redux-mock-store';
import { Provider } from 'react-intl-redux';

const mockStore = configureStore();

global.__SERVER__ = true; // eslint-disable-line no-underscore-dangle
global.__CLIENT__ = false; // eslint-disable-line no-underscore-dangle

jest.mock('~/config', async () => {
  const { blockTagDeserializer } = await import(
    '../editor/deserialize' // or 'volto-slate/editor/deserialize'
  );

  const createEmptyParagraph = () => {
    return {
      type: 'p',
      children: [{ text: '' }],
    };
  };
  return {
    settings: {
      supportedLanguages: [],
      slate: {
        elements: {
          default: ({ attributes, children }) => (
            <p {...attributes}>{children}</p>
          ),
          strong: ({ children }) => {
            return <strong>{children}</strong>;
          },
        },
        leafs: {},
        defaultBlockType: 'p',
        textblockExtensions: [],
        extensions: [
          (editor) => {
            editor.htmlTagsToSlate = {
              STRONG: blockTagDeserializer('strong'),
            };
            return editor;
          },
        ],
        defaultValue: () => {
          return [createEmptyParagraph()];
        },
      },
    },
  };
});

window.getSelection = () => ({});

test('renders a WysiwygWidget component', () => {
  const store = mockStore({
    intl: {
      locale: 'en',
      messages: {},
    },
  });
  const component = renderer.create(
    <Provider store={store}>
      <WysiwygWidget
        id="qwertyu"
        title="My Widget"
        description="My little description."
        required={true}
        value={{ data: 'abc <strong>def</strong>' }}
        onChange={(id, data) => {
          // console.log('changed', data.data);
          // setHtml(data.data);
        }}
      />
    </Provider>,
  );
  const json = component.toJSON();
  expect(json).toMatchSnapshot();
});

What I've tried我试过的

  1. The code snippets above show partially what I have tried.上面的代码片段部分显示了我尝试过的内容。
  2. I searched the web for 'jest mock async await import' and did not found something relevant.我在 web 中搜索了“jest mock async await import”,但没有找到相关内容。

The question问题

If jest.mock is not made to work with async , what else can I do to make my unit test work?如果jest.mock不能与async一起工作,我还能做些什么来使我的单元测试工作?

Update 1更新 1

In the last snippet of code above, the line在上面的最后一段代码中,该行

              STRONG: blockTagDeserializer('strong'),

uses blockTagDeserializer defined here which uses deserializeChildren , createEmptyParagraph (which is imported from another module), normalizeBlockNodes (which is imported from another module) and jsx (which is imported from another module) functions, which use deserialize which uses isWhitespace which is imported from another module and typeDeserialize which uses jsx and deserializeChildren .使用这里定义的blockTagDeserializer ,它使用deserializeChildrencreateEmptyParagraph (从另一个模块导入)、 normalizeBlockNodes (从另一个模块导入)和jsx (从另一个模块导入)函数,它们使用deserialize化,它使用从另一个导入的isWhitespace使用jsxdeserializeChildren的模块和typeDeserialize

Without using await import(...) syntax how can I fully mock the module so that my unit test works?如果不使用await import(...)语法,我如何才能完全模拟模块以便我的单元测试工作?

If you want to dig into our code, please note that the volto-slate/ prefix in the import statements is for the src/ folder in the repo.如果您想深入了解我们的代码,请注意import语句中的volto-slate/前缀是针对 repo 中的src/文件夹的。

Thank you.谢谢你。

I'd advise not doing any "heavy" stuff (whatever that means) in a callback of jest.mock , – it is designed only for mocking values.我建议不要在jest.mock的回调中做任何“繁重”的事情(无论这意味着什么),它仅适用于 mocking 值。

Given your specific example, I'd just put whatever the output of blockTagDeserializer('strong') right inside the config:鉴于您的具体示例,我只需将blockTagDeserializer('strong')的 output 放在配置中:

jest.mock('~/config', () => {
  // ...

        extensions: [
          (editor) => {
            editor.htmlTagsToSlate = {
              STRONG: ({ children }) => <strong>{children}</strong>, // or whatever this function actually returns for 'strong'
            };
            return editor;
          },
        ],

  // ...
});

This doesn't require anything asynchronous to be done.这不需要任何异步操作。

If you need this setup to be present in a lot of files, extracting it in a setup file seems to be the next best thing.如果您需要此设置存在于许多文件中,则将其提取到设置文件中似乎是下一个最佳选择。

I found a solution.我找到了解决方案。 I have a ref callback that sets the htmlTagsToSlate property of the editor in the actual code of the module, conditioned by global.__JEST__ which is defined as true in Jest command line usage:我有一个ref回调,它在模块的实际代码中设置editorhtmlTagsToSlate属性,以global.__JEST__为条件,在 Jest 命令行使用中定义为true

import { htmlTagsToSlate } from 'volto-slate/editor/config';

[...]

testingEditorRef={(val) => {
  ref.current = val;
  if (val && global.__JEST__) {
    val.htmlTagsToSlate = { ...htmlTagsToSlate };
  }
}}

Now the jest.mock call for ~/config is simple, there is no need to do an import in it.现在对~/configjest.mock调用很简单,不需要在其中进行import

I also use this function:我也使用这个 function:

const handleEditorRef = (editor, ref) => {
  if (typeof ref === 'function') {
    ref(editor);
  } else if (typeof ref === 'object') {
    ref.current = editor;
  }
  return editor;
};

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM