首页 > 解决方案 > 覆盖 JS 中特定单元测试用例的 sinon 存根

问题描述

我有一个从 json 文件读取后返回产品列表的函数。

我创建了一个存根来模拟 fs.readfile 的行为,但我想检查函数抛出的异常,为此我想覆盖默认存根以返回 null。我如何实现这一目标

我的功能

async getAllProducts() {
        try {
            // let rawData = fs.readFileSync("data/products.json");
            // let data = JSON.parse(rawData);
            // return data;
            return JSON.parse(await fs.promises.readFile("data/products.json"));
        } catch (error) {
            if (error.message === "Unexpected end of JSON input") {
                throw new NoProductsExistError("The File is Empty");
            }
            throw new FileReadingError("Error Reading File");
        }
    }

我的 spec.js 文件

// const assert = require("assert");
const chai = require("chai");
const expect = chai.expect;
const sinon = require("sinon");
const dao = require("./dao");
const fs = require("fs");

var sandbox;

beforeEach(() => {
    sandbox = sinon.createSandbox();
    sandbox
        .stub(fs.promises, "readFile")
        .withArgs("data/products.json")
        .returns(
            JSON.stringify([
                {
                    productId: 101,
                    productName: "Sony XB450AP Wired Headset",
                },
                {
                    productId: 102,
                    productName: "Sony 1000XM3 Wired Headset",
                }
            ])
        );

});

describe("getAllProducts", () => {
    it("should return all products", async () => {
// Here we are using the default stub of sinon got from the beforeEach
        expect(await dao.getAllProducts()).to.deep.equal([
                {
                    productId: 101,
                    productName: "Sony XB450AP Wired Headset",
                },
                {
                    productId: 102,
                    productName: "Sony 1000XM3 Wired Headset",
                }
            ]);
    });

    it("should throw Error on Empty File", async () => {
// WANT TO OVERRIDE THE DEFAULT STUB TO RETURN NOTHING
// BELOW STUB DOES NOT WORK AND GIVES "TypeError: Attempted to wrap readFile which is already wrapped" ERROR
        sinon
            .stub(fs.promises, "readFile")
            .withArgs("data/products.json")
            .returns();

        expect(await dao.getAllProducts()).to.throw(NoProductsExistError);
    });
});

如何使第二个存根工作。任何帮助深表感谢

标签: javascriptunit-testingmocha.jssinon

解决方案


如果您只对依赖项进行一次存根/模拟,然后在每个测试用例之前重置该存根/模拟,则更好地进行测试。定义依赖项应该为每个测试用例做什么。

// const assert = require("assert");
const chai = require("chai");
const expect = chai.expect;
const sinon = require("sinon");
const dao = require("./dao");
const fs = require("fs");


describe("getAllProducts", () => {

    var sandbox;
    var fsReadFileStub;

    before(() => {
        sandbox = sinon.createSandbox();
        fsReadFileStub = sandbox.stub(fs.promises, "readFile")

    });

    afterEach(() => {
        fsReadFileStub.reset();
    })

    it("should return all products", async () => {
        fsReadFileStub.withArgs("data/products.json")
            .returns(
                JSON.stringify([
                    {
                        productId: 101,
                        productName: "Sony XB450AP Wired Headset",
                    },
                    {
                        productId: 102,
                        productName: "Sony 1000XM3 Wired Headset",
                    }
                ])
            );
        expect(await dao.getAllProducts()).to.deep.equal([
            {
                productId: 101,
                productName: "Sony XB450AP Wired Headset",
            },
            {
                productId: 102,
                productName: "Sony 1000XM3 Wired Headset",
            }
        ]);
    });

    it("should throw Error on Empty File", async () => {
        fsReadFileStub
            .stub(fs.promises, "readFile")
            .withArgs("data/products.json")
            .returns();

        expect(await dao.getAllProducts()).to.throw(NoProductsExistError);
    });
});

推荐阅读