Search code examples
javascriptunit-testingmocha.jssinon

Override sinon stub for a particular unit test case in JS


I have a function which returns list of products after reading from a json file.

I have created a stub to mock the behaviour of fs.readfile but I want to check the exception thrown from the function and for that I want to override the default stub to return null. How do I achieve this

my function

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");
        }
    }

my spec.js file

// 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);
    });
});

How do I make the second stub work. Any help is much appreciated


Solution

  • It is better for testing if you stub/mock a dependency only once and then reset that stub/mock before each test case. Define what the dependency should do for each test case.

    // 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);
        });
    });