PHP前端开发

笑话回顾:安全模拟全局对象的属性和方法

百变鹏仔 3天前 #JavaScript
文章标签 全局

长话短说:

  • 对于全局对象,您应该使用
  • 确保在 aftereach() 钩子中调用 jest.restoreallmocks() 。
  • 什么?

    在完美的世界代码库中,不需要操作全局对象,但是世界代码库很混乱 - 测试也是如此。

    您要不惜一切代价避免一个测试影响另一个测试。无论顺序如何,或者是否跳过某些测试,测试都应该有意义。

    模拟属性

    模拟值的一种简单方法是将属性设置为测试中所需的任何值。
    只要您更改此特定测试拥有(创建)的本地对象中的值就可以了:

    describe("override properties of local objects", () => {    it("works and is harmless", () => {        const myarray = [1];        myarray.length = 0;        expect(myarray).tohavelength(0);    });    it("does not affect the next test", () => {        const myarray = [1];        expect(myarray).tohavelength(1);    });});

    如果你对全局对象这样做,它会变得混乱:

    describe("don't override properties of global objects", () =&gt; {    it("works before the property is overridden", () =&gt; {        expect(window.innerwidth).tobegreaterthan(0);    });    it("works, but is evil", () =&gt; {        window.innerwidth = 0;        expect(window.innerwidth).tobe(0);    });    it("fails in the test after the property was overridden", () =&gt; {        expect(() =&gt; {            expect(window.innerwidth).tobegreaterthan(0); // <p>这就是 jest.replaceproperty() 的用途:<br></p><pre class="brush:php;toolbar:false">describe("use jest.replaceproperty() to override properties of global objects", () =&gt; {    aftereach(() =&gt; {        jest.restoreallmocks();    });    it("works before the property is overridden", () =&gt; {        expect(window.innerwidth).tobegreaterthan(0);    });    it("works and is harmless", () =&gt; {        jest.replaceproperty(window, "innerwidth", 0);        expect(window.innerwidth).tobe(0);    });    it("does not affect the next test", () =&gt; {        expect(window.innerwidth).tobegreaterthan(0);    });});

    模拟方法

    方法可以像属性一样被模拟。

    describe("override methods of local objects using jest.fn()", () =&gt; {    it("works and is harmless", () =&gt; {        const myset = new set([1]);        myset.has = jest.fn().mockreturnvalue(false);        expect(myset.has(1)).tobefalsy();    });    it("does not affect the next test", () =&gt; {        const myset = new set([1]);        expect(myset.has(1)).tobetruthy();    });});

    如果你在全局对象上使用 myobject.somefunction = jest.fn() ,你的测试可能会相互依赖并失去它们的意义:

    describe("don't override methods of global objects using jest.fn()", () =&gt; {    it("works before the method is overridden", () =&gt; {        expect(document.getelementbyid("foo")).tobenull();    });    it("works, but is evil", () =&gt; {        const el = document.createelement("div");        document.getelementbyid = jest.fn().mockreturnvalue(el);        expect(document.getelementbyid("foo")).tobe(el);    });    it("fails in the test after the property was overridden", () =&gt; {        expect(() =&gt; {            expect(document.getelementbyid("foo")).tobenull(); // <p>我们应该如何模拟全局对象中的方法?这就是 jest.spyon() 的好处:<br></p><pre class="brush:php;toolbar:false">describe("use jest.spyOn() to override methods of global objects", () =&gt; {    afterEach(() =&gt; {        jest.restoreAllMocks();    });    it("works before the method is overridden", () =&gt; {        expect(document.getElementById("foo")).toBeNull();    });    it("works and is harmless", () =&gt; {        const el = document.createElement("div");        jest.spyOn(document, "getElementById").mockReturnValue(el);        expect(document.getElementById("foo")).toBe(el);    });    it("does not affect the next test", () =&gt; {        expect(document.getElementById("foo")).toBeNull();    });});

    你必须清理

    如果你想确保所有测试都发现系统处于相同(新鲜、干净)状态,则需要在每次测试后恢复模拟的状态。

    最简单的解决方案是设置restoremocks配置属性。

    最直接的选择是在 aftereach() 中调用 jest.restoreallmocks()

    如何为所有测试模拟某些内容

    有时您想模拟文件中所有测试的内容。如果您在顶层或在describe()块中使用jest.spyon()和jest.replaceproperty(),则在执行第一个测试后,所有mock将被重置。

    在顶层,您可以安全地重写属性和方法,无需 jest.spyon() 和 jest.replaceproperty()。

    如果您只想为describe() 块模拟事物,则需要在beforeeach() 挂钩中进行这些调用。