更改模块对象时,为什么不更改花括号导入? [英] When mutating a module object, why are curly brace imports not changed?

查看:40
本文介绍了更改模块对象时,为什么不更改花括号导入?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在整理代码库,该代码库执行一些直接的模块突变,而不是使用任何模拟行为,并且我注意到了一些奇怪的事情.

I'm playing around with tidying up a code base that does some direct module mutations, rather than using any mocking behaviour, and I've noticed something odd.

如果我从一个全新的create-react-app开始,请在测试中执行以下操作:

If I, starting with a fresh create-react-app do something like this in a test:

import React from 'react';
import { render } from '@testing-library/react';
import App from './App';
import lodash from "lodash"; 
test('renders learn react link', () => {

  // Directly mutating the module
  React.useContext = jest.fn();
  React.foo = "foo";
  lodash.drop = jest.fn(); 

  const { getByText } = render(<App />);
  const linkElement = getByText(/learn react/i);
  expect(linkElement).toBeInTheDocument();
});

然后当我以后去使用这些突变时:

Then when I later go to use these mutations:

import React, {useContext, foo} from 'react';
import logo from './logo.svg';
import './App.css';
import lodash, {drop} from "lodash"; 
function App() {


  console.log(useContext, React.useContext, foo, React.foo );
  console.log(drop, lodash.drop); 
  // The rest doesn't matter. 

然后我们可以看到, React.useContext React.foo lodash.drop 将打印出笑话功能,而 useContext foo drop 将打印原始对象.

Then what we can see is that the React.useContext, React.foo, lodash.drop will print the jest mock function, whereas the useContext, foo and drop will print the original object.

完整打印:

  console.log src/App.js:8
    [Function: useContext] [Function: mockConstructor] {
      _isMockFunction: true,
      getMockImplementation: [Function (anonymous)],
      mock: [Getter/Setter],
      mockClear: [Function (anonymous)],
      mockReset: [Function (anonymous)],
      mockRestore: [Function (anonymous)],
      mockReturnValueOnce: [Function (anonymous)],
      mockResolvedValueOnce: [Function (anonymous)],
      mockRejectedValueOnce: [Function (anonymous)],
      mockReturnValue: [Function (anonymous)],
      mockResolvedValue: [Function (anonymous)],
      mockRejectedValue: [Function (anonymous)],
      mockImplementationOnce: [Function (anonymous)],
      mockImplementation: [Function (anonymous)],
      mockReturnThis: [Function (anonymous)],
      mockName: [Function (anonymous)],
      getMockName: [Function (anonymous)]
    } undefined foo

  console.log src/App.js:9
    [Function: drop] [Function: mockConstructor] {
      _isMockFunction: true,
      getMockImplementation: [Function (anonymous)],
      mock: [Getter/Setter],
      mockClear: [Function (anonymous)],
      mockReset: [Function (anonymous)],
      mockRestore: [Function (anonymous)],
      mockReturnValueOnce: [Function (anonymous)],
      mockResolvedValueOnce: [Function (anonymous)],
      mockRejectedValueOnce: [Function (anonymous)],
      mockReturnValue: [Function (anonymous)],
      mockResolvedValue: [Function (anonymous)],
      mockRejectedValue: [Function (anonymous)],
      mockImplementationOnce: [Function (anonymous)],
      mockImplementation: [Function (anonymous)],
      mockReturnThis: [Function (anonymous)],
      mockName: [Function (anonymous)],
      getMockName: [Function (anonymous)]
    }

 PASS  src/App.test.js (6.875s)
  ✓ renders learn react link (304ms)


这是为什么?

我认为这与导入/请求解析导入的方式有关,并且这样做:

I assume that it's something to do with the way import/require is resolving the imports, and that doing:

import React, {useContext} from "react"

与操作不同

import React from "react"; 
const {useContext} = React

与直觉相反.

推荐答案

这与创建变量/常量的时间有关.

This is related to When the variables/constants are created.

执行"导入在对对象和常量foo进行突变之前,然后在对React.foo进行突变之前创建了

Import is "executed" before you mutate your object and the constant foo, is then created before you mutate React.foo.

const obj = {a: 1, b: 2};
const a = obj.a; // import
obj.a = 42; // your mock injection

console.log(a); // it will keep returning 1    

此方法的处理方式是,创建 a (在您的示例中为 foo useContext )时,它指向该位置由 obj.a 保留的内存,在创建时包含 1 .然后 obj.a 指向存储 42 的内存的新位置,但是 obj.a a 彼此独立.

The way this is handled, is that when a (foo or useContext in your example) is created, it points to the position of memory hold by obj.a, which contains a 1 in the moment of creation. Then obj.a points to a new position of memory where a 42 is stored, but obj.a and a are independent one to another.

在您的情况下,我的建议是使用依赖注入解决方案.

In your case my recommendation is to use a Dependency Injection solution.

每个App都会收到需要其依赖的参数,然后在测试中对其进行模拟.< App foo = {myFooMock}/> 或另一种常见的解决方案是发送上下文,并为带有模拟的Tests的上下文创建者和为生产应用程序的上下文创建者.

Either App receive a parameter for which dependency it needs, and then you mock it in the test. <App foo={myFooMock} /> or another usual solution is to sent a context, and have a context creator for Tests with mocks and a context creator for production app.

const myMockedContext = {foo: 'foo', useContext: jest.fn()};
const { getByText } = render(<App context={myMockedContext} />);

通常,您会希望在测试中拥有尽可能多的真实代码,所有业务逻辑,以便一旦接口发生更改或类型返回错误或您之间的任何其他意外行为,测试就会失败不同的代码区域.

Normally you would want to have as much of your real code in your test as possible, all your business logic so that your test fail as soon as there are changes in interfaces or error in type returns or any other unexpected behaviour among your different areas of code.

但这是TDD社区中的Mockist和Classists之间正在进行的讨论.

But this is an ongoing discussion between Mockist and Classists in the TDD community.

HTH

这篇关于更改模块对象时,为什么不更改花括号导入?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆