I tried stubbing functions that were in a controller in an express app and when I called the controller function, assertions I wrote throws an error that the stubbed functions are not called or not called with the required arguments. Below is the test file. When I console.log(createUserStub) I get [Function: createUser]. Also, when I tried console.logging the newUser generated in the controller function, the result printed out shows that the actual internal function, createUser was called and not the stubbed function.
const sinon = require("sinon");
const sinonChai = require("sinon-chai");
const chai = require("chai");
chai.use(sinonChai);
const expect = chai.expect;
const authController = require("./../../src/controllers/auth");
const tokenService = require("./../../src/services/token.js");
const authService = require("./../../src/services/auth.js");
const { generatePassword } = require("./../../src/utils/auth.js");
const sendEmailModule = require("./../../src/utils/sendEmail.js");
describe("Signup Controller", () => {
let res, req, createUserStub, generateTokenStub, sendEmailStub;
let sandbox;
const fakeHashedPassword = "fhfhgjkggklgj";
const fakeId = "hjklkjhghjkjhghjkjh";
const fakePictureUrl = "www.picture.com";
const fakeToken = "rhjkljhghjkjhghj";
const userInput = {
name: "Emmanuel Ibekwe",
email: "[email protected]",
password: generatePassword(),
picture: fakePictureUrl
};
const newUser = {
_id: fakeId,
name: "Emmanuel Ibekwe",
email: "[email protected]",
picture: fakePictureUrl
};
const fakeCreatedUser = {
...userInput,
password: fakeHashedPassword,
_id: fakeId
};
beforeEach(() => {
sandbox = sinon.createSandbox();
console.log("inside beforeEach");
createUserStub = sandbox.stub(authService, "createUser");
createUserStub.resolves(fakeCreatedUser);
generateTokenStub = sandbox.stub(tokenService, "generateToken");
generateTokenStub.resolves(fakeToken);
sendEmailStub = sandbox.stub(sendEmailModule, "sendEmail").resolves();
});
afterEach(() => {
console.log("inside afterEach");
sandbox.restore();
});
it("should sign up user successfully", async done => {
const statusJsonSpy = sandbox.spy();
res = {
json: sandbox.spy(),
status: sandbox.stub().returns({ json: statusJsonSpy })
};
req = { body: userInput };
const next = err => {
console.log("err", err);
};
try {
await authController.signup(req, res, next);
// createUserStub();
expect(createUserStub).to.have.been.calledOnce;
expect(createUserStub).to.have.been.calledOnceWith(userInput);
expect(generateTokenStub).to.have.been.calledTwice;
expect(sendEmailStub).to.have.been.calledOnce;
expect(res.status).to.have.been.calledOnceWith(201);
expect(res.json).to.have.been.calledWith({
message: "sign up successful",
accessToken: fakeToken,
refreshToken: fakeToken,
user: newUser
});
done();
} catch (error) {
done(error);
}
}); //
});
This is the controller
const { createUser, signInUser } = require("./../services/auth.js");
const { generateToken, verifyToken } = require("./../services/token.js");
const createHttpError = require("http-errors");
const User = require("./../models/user.js");
const { sendEmail } = require("./../utils/sendEmail.js");
const PasswordResetCode = require("./../models/PasswordResetCode.js");
const bcryptjs = require("bcryptjs");
const { isPasswordFalse } = require("../utils/validation.js");
const { getRandomSixDigit, generatePassword } = require("./../utils/auth.js");
const dotenv = require("dotenv");
const { OAuth2Client } = require("google-auth-library");
dotenv.config();
const {
ADMIN_EMAIL,
FRONT_END_TESTING_DOMAIN,
FRONT_END_PRODUCTION_DOMAIN
} = process.env;
const signup = async (req, res, next) => {
try {
const { name, email, password, picture } = req.body;
console.log("req.body", { name, email, password, picture });
const newUser = await createUser({
name,
email,
password,
picture
});
console.log("newUser", newUser);
const accessToken = await generateToken(
{
userId: newUser._id.toString(),
email: newUser.email
},
"1d",
process.env.ACCESS_TOKEN_SECRET
);
console.log("accessToken", accessToken);
const refreshToken = await generateToken(
{
userId: newUser._id.toString(),
email: newUser.email
},
"30d",
process.env.REFRESH_TOKEN_SECRET
);
await sendEmail({
code: null,
to: newUser.email,
subject: "Trackr Sign up",
name: newUser.name.split(" ")[0],
type: "sign up"
});
console.log(201);
res.status(201).json({
message: "sign up successful",
accessToken,
refreshToken,
user: {
_id: newUser._id,
name: newUser.name,
email: newUser.email,
picture: newUser.picture
}
});
} catch (error) {
if (!error.status) {
error.status = 500;
}
next(error);
}
};
Here are the codes of the internal functions.
const createHttpError = require("http-errors");
const validator = require("validator");
const bcryptjs = require("bcryptjs");
const User = require("../models/user.js");
const validation = require("./../utils/validation.js");
const { isPasswordFalse } = validation;
const createUser = async userData => {
const { name, email, password, picture } = userData;
// console.log("userData", userData);
if (!name || !email || !password) {
throw createHttpError.BadRequest("Please fill all fields");
}
// console.log("name", name);
if (!validator.isEmail(email)) {
throw createHttpError.BadRequest("email is invalid");
}
const checkEmail = await User.findOne({ email: email });
if (checkEmail) {
throw createHttpError.Conflict("email already exists. Try a new email.");
}
if (isPasswordFalse(password)) {
throw createHttpError.BadRequest(
"password must be atleast 8 characters and contain atleast an uppercase, a lowercase, a number or a special character"
);
}
const hashedPassword = await bcryptjs.hash(password, 12);
// console.log("hashedPassword", hashedPassword);
const user = await new User({
name,
email,
password: hashedPassword,
picture: picture || DEFAULT_PICTURE_URL
});
return user.save();
};
const tokenUtils = require("./../utils/token.js");
const { sign, verify } = tokenUtils;
const generateToken = async (payload, expiresIn, secret) => {
const token = await sign(payload, expiresIn, secret);
return token;
};
I tried testing just the createUser function and all tests were passed. Here is the test file.
const mongoose = require("mongoose");
const sinon = require("sinon");
const sinonChai = require("sinon-chai");
const chai = require("chai");
chai.use(sinonChai);
const expect = chai.expect;
const authService = require("../../src/services/auth.js");
const bcryptjs = require("bcryptjs");
const User = require("../../src/models/user");
const { generatePassword } = require("../../src/utils/auth");
describe("createUser Service", () => {
let hashStub, findOneStub, saveStub, sandbox;
const fakeHashedPassword = "fhfhgjkggklgj";
const fakeId = "hjklkjhghjkjhghjkjh";
const fakePictureUrl = "www.picture.com";
const userInput = {
name: "Emmanuel Ibekwe",
email: "[email protected]",
password: generatePassword(),
picture: fakePictureUrl
};
const fakeCreatedUser = {
...userInput,
password: fakeHashedPassword,
_id: fakeId
};
beforeEach(() => {
sandbox = sinon.createSandbox();
hashStub = sandbox.stub(bcryptjs, "hash");
findOneStub = sandbox.stub(mongoose.Model, "findOne");
saveStub = sandbox.stub(User.prototype, "save");
hashStub.resolves(fakeHashedPassword);
findOneStub.resolves(null);
saveStub.resolves(fakeCreatedUser);
});
afterEach(() => {
sandbox.restore();
});
it("should return a newly created user", async () => {
newUser = await authService.createUser(userInput);
expect(newUser).to.deep.equal(fakeCreatedUser);
});
it("should throw a BadRequest error if name is not provided", async () => {
try {
await authService.createUser({ ...userInput, name: undefined });
} catch (err) {
// console.log(err);
expect(err.status).to.equal(400);
expect(err.message).to.equal("Please fill all fields");
}
});
it("should throw a BadRequest error if email is invalid", async () => {
try {
await authService.createUser({ ...userInput, email: "undefined" });
} catch (err) {
// console.log(err);
expect(err.status).to.equal(400);
expect(err.message).to.equal("email is invalid");
}
});
it("should throw a BadRequest error if email is not provided", async () => {
try {
await authService.createUser({ ...userInput, email: undefined });
} catch (err) {
// console.log(err);
expect(err.status).to.equal(400);
expect(err.message).to.equal("Please fill all fields");
}
});
it("should throw a Conflict error if user already exists", async () => {
try {
await authService.createUser({ ...userInput, password: "hhfjdkdlflf" });
} catch (err) {
// console.log(err);
expect(err.status).to.equal(400);
expect(err.message).to.equal(
"password must be atleast 8 characters and contain atleast an uppercase, a lowercase, a number or a special character"
);
}
});
it("should throw a BadRequest error if password does not meet the required conditions", async () => {
findOneStub.resolves(fakeCreatedUser);
try {
await authService.createUser(userInput);
} catch (err) {
expect(err.status).to.equal(409);
expect(err.message).to.equal("email already exists. Try a new email.");
}
});
});