编程语言
首页 > 编程语言> > javascript-如果从另一个模块调用该模块,为什么不对一个模块进行突变,而不是从其自身调用,该变量为什么会更新引用呢?

javascript-如果从另一个模块调用该模块,为什么不对一个模块进行突变,而不是从其自身调用,该变量为什么会更新引用呢?

作者:互联网

这个问题与测试javascript和模拟功能有关.

说我有一个看起来像这样的模块:

export function alpha(n) {
    return `${n}${beta(n)}${n}`;
}

export function beta(n) {
    return new Array(n).fill(0).map(() => ".").join("");
}

然后我无法通过以下方式对其进行测试:

import * as indexModule from "./index";

//Not what we want to do, because we want to mock the functionality of beta
describe("alpha, large test", () => {
    it("alpha(1) returns '1.1'", () => {
        expect(indexModule.alpha(1)).toEqual("1.1"); //PASS
    });

    it("alpha(3) returns '3...3'", () => {
        expect(indexModule.alpha(3)).toEqual("3...3"); //PASS
    });
});

//Simple atomic test
describe("beta", () => {
    it("beta(3) returns '...'", () => {
        expect(indexModule.beta(3)).toEqual("..."); //FAIL: received: 'x'
    });
});

//Here we are trying to mutate the beta function to mock its functionality
describe("alpha", () => {

    indexModule.beta = (n) => "x";
    it("works", () => {
        expect(indexModule.alpha(3)).toEqual("3x3"); //FAIL, recieved: '3...3'
    });
});

但是,如果将模块分为两部分:

alpha.js

import { beta } from "./beta";

export function alpha(n) {
    return `${n}${beta(n)}${n}`;
}

beta.js

export function beta(n) {
    return new Array(n).fill(0).map(() => ".").join("");
}

然后,我可以更改beta模块,而alpha知道这一点:

import { alpha } from "./alpha";
import * as betaModule from "./beta";

describe("alpha", () => {
    betaModule.beta = (n) => "x";
    it("works", () => {
        expect(alpha(3)).toEqual("3x3");   //PASS
    });
});

为什么会这样呢?我正在寻找技术上特定的答案.

我有一个Github分支,代码为here,请参见mutateModule和singleFunctionPerModuleAndMutate文件夹.

另一个问题-在此示例中,我通过直接重新分配属性来对模块进行变异.我理解使用Jest模拟功能本质上会做同样的事情,对吗?

即.如果第一个示例不起作用而第二个示例不起作用的原因是由于该突变,那么它恰好意味着使用jest模块的模拟功能同样行不通.

据我所知-测试模块as this jest github issues talks about时无法模拟模块中的单个功能.我想知道-这就是为什么.

解决方法:

Why does mutating a module update the reference if calling that module from another module, but not if calling from itself?

“In ES6, imports are live read-only views on exported values”.

导入ES6模块时,您实际上可以实时查看该模块导出的内容.

实时视图可以更改,任何导入模块导出实时视图的代码都可以看到该更改.

这就是为什么当alpha和beta在两个不同的模块中时您的测试有效的原因.该测试会修改beta模块的实时视图,并且由于alpha模块使用beta模块的实时视图,因此它会自动使用模拟功能而不是原始功能.

另一方面,在上面的代码中,alpha和beta在同一模块中,alpha直接调用beta. alpha不使用模块的实时视图,因此当测试修改模块的实时视图时,它无效.

As an additional question – in this example I am mutating the module by directly reassigning properties. Am I right in understanding that using jest mock functionality is going to be essentially doing the same thing?

有几种使用Jest模拟事物的方法.

一种方法是使用jest.spyOn,它接受一个对象和一个方法名称,以及replaces the method on the object with a spy that calls the original method.

使用jest.spyOn的一种常见方法是将ES6模块的实时视图作为对象传递给它,以使模块的实时视图发生变化.

因此,是的,通过将ES6模块的实时视图传递给jest.spyOn(或Jasmine的spyOn或Sinon的sinon.spy等)进行模拟,以本质上与直接更改实时视图相同的方式来更改模块的实时视图.就像您在上面的代码中所做的一样查看模块.

As far as I know – there is not way to mock a single function in a module, while testing that module, as this jest github issues talks about. What I’m wanting to know – is why this is.

实际上,这是可能的.

“ES6 modules support cyclic dependencies automatically”,这意味着可以将模块的实时视图导入模块本身.

只要alpha使用定义了beta的模块的实时视图调用beta,就可以在测试期间模拟beta.即使它们在同一模块中定义,此方法也有效:

import * as indexModule from './index'  // import the live view of the module

export function alpha(n) {
    return `${n}${indexModule.beta(n)}${n}`;  // call beta using the live view of the module
}

export function beta(n) {
    return new Array(n).fill(0).map(() => ".").join("");
}

标签:es6-modules,javascript,jestjs
来源: https://codeday.me/bug/20191011/1894700.html