Search code examples
node.jstestingmocha.jschaisinon

Mocha/Chai expect an error when calling an async function


Im using mocha / chai to to do some testing. The function is supposed to throw new Error if called with the same arguments.

The function:

Create a new user and save it do my database (mongoDB), if a user with the same discord id doesn't exist already. fetchUser(disc_id)searches for an existing user, by using findOne() from mongoose.

async function createUser(disc_id, moodleToken) {
    const profileData = await fetchUser(disc_id);
    if (profileData) {
        throw new Error("The account already exist in the database!");
    }
    
    const newUser = profileModel.create({
        discord_id: disc_id,
        moodle_token: moodleToken,
    }, (err) => {
            if (err) throw err;
            console.log("Document created!")
    })
    return newUser;
};

fetchUser

function fetchUser (disc_id) {
    return profileModel.findOne({ discord_id: disc_id });
}

Testing

Im using sinon to create the "test-database". The following test passes just fine

describe("Create user", function () {
        it("Should create a new user in database if the discord id is not in the database", async function () {
            const discord_id = "2341243451";
            const moodle_token = "fwefw89fwefHFEF0F90ls";
            
            sinon.stub(profileSchema, "findOne").returns(null);
            sinon.stub(profileSchema, "create").returns({
                discord_id, moodle_token
            });
            
            const returnedUser = await createUser(discord_id, moodle_token);
            
            expect(returnedUser.discord_id).to.equal(discord_id);
        })

This tests that it is possible to create and save a new user to my database, if no existing user has the same discord id.

If I want to test the opposite, to create a new user, but a current user already exists with the same id, it should throw the error: "The account already exist in the database!"

it("Should throw an error if there exists a user with that discord id", async () => {
            const discord_id = "2341243451";
            const moodle_token = "fwefw89fwefHFEF0F90ls";
            
            const fakeObject = {
                discord_id: discord_id,
                moodle_token: moodle_token
            };

            sinon.stub(profileSchema, "findOne").returns(fakeObject);
            sinon.stub(profileSchema, "create").returns({ discord_id, moodle_token })

I have tried to following without success:

            try {
                await createUser(discord_id, moodle_token);
            } catch (err) {
                console.log(err);
                expect(err).to.equal("The account already exist in the database!");
            }
            
            expect(async function () { await createUser(discord_id, moodle_token); }).to.throw("The account already exist in the database!");
            expect(() => { createUser(discord_id, moodle_token)}).to.throw("The account already exist in the database!");
            rejects(async () => { await createUser(discord_id, moodle_token); })

How should i test such a function?


Solution

  • I found a way to solve the problem, i restructured my createUser() to this:

    async function createUser(disc_id, moodleToken) {
        const profileData = await fetchUser(disc_id);
        if (profileData) {
            throw TypeError("The account already exist in the database!");
        }
        const newUser = new profileModel({
            discord_id: disc_id,
            moodle_token: moodleToken,
        })
        await newUser.save();
        return newUser;
    }
    

    The test passes if they are like this:

    it("Should throw an error if there exists a user with that discord id", async () => {
                const discord_id = "2341243451";
                const moodle_token = "fwefw89fwefHFEF0F90ls";
                
                const fakeObject = {
                    discord_id: "2341243451",
                    moodle_token: "fwefw89fwefHFEF0F90ls"
                };
    
                sinon.stub(profileSchema, "findOne").returns(fakeObject);
    
                await createUser(discord_id, moodle_token).catch((error) => {
                    expect(error.message).to.equal("The account already exist in the database!")
                })
    

    Don't know if it is the right way to do it, but if i change sinon.stub to return null the test wont pass.

    So i guess this is a way to handle it.