My tests are working and passing, but chai's done function is messing up. I've tried a few different methods, but I don't understand where I'm going wrong in these unit tests. I'm very new to unit testing and chai, so any help would be greatly appreciated
The places where the test is failing: line 40 (create), line 98 (update)
Any ideas?
const chai = require('chai')
let should = chai.should()
let expect = chai.expect
let db = require('../app/models')
db.hosts.modelName = 'Hosts'
db.victims.modelName = 'Victims'
let models = [db.hosts, db.victims]
models.forEach(model => {
describe(`${model.modelName} Model`, function (done) {
var modelData = {
guest_count: 3,
start_date: "2018-01-11T00:00:00.000Z",
end_date: "2018-01-12T00:00:00.000Z",
location: {
type: "Point",
coordinates: [
-74.323564,
40.232323
]
},
first_name: "Sean",
last_name: "Destroyed",
phone: "7325556677",
address: "123 main street, red bank, nj",
email: "test#gmail.com",
}
it(`should create a new ${model.modelName}`, function () {
model.create(modelData).then(function (user) {
//victim name should be equivalent to the fake submission we are using
expect(user.first_name).to.equal("Sean");
//remove the entry from the database
model.destroy({
where: {
id: user.id
}
})
done()
})
});
it(`should delete a ${model.modelName} from the database`, function () {
model.create(modelData).then(function (user) {
//victim name should be equivalent to the fake submission we are using
//remove the entry from the database
model.destroy({
where: {
id: user.id
}
})
try {
model.findOne({
where: {
id: user.id
}
})
} catch (err) {
expect(user.first_name).to.undefined;
if (err) {
done()
}
}
})
})
it(`should update the ${model.modelName} entry in the database`, function () {
model.create(modelData).then(function (user) {
//after user is created, then update a value
modelData.guest_count = 12
model.update(modelData, {
where: {
id: user.id
}
}).then(function(data) {
model.findOne({
where: {
id: user.id
}
}).then(function (data) {
expect(data.guest_count).to.equal(12);
}).then(function () {
model.destroy({
where: {
id: user.id
}
})
}).then(function() {
done()
})
})
})
})
})
});
There are two things to keep in mind:
(1) Sequelize uses promises for its ORM methods. So, even after you are calling destroy, you need to attach a callback, e.g.:
model.destroy({
where: {
id: user.id
}
})
.then(function() {
// now do something
});
(2) The done method in chai should be attached to each test, as opposed to the test block:
describe('some test block', function() {
it('should do something,' function(done) {
User.findAll().then(function(users) {
// expect users to do something
done(); // tests are done
});
});
});
In your case, here are the two failing test cases:
// ensure "destroy" has a callback
it(`should create a new ${model.modelName}`, function (done) {
model.create(modelData).then(function (user) {
//victim name should be equivalent to the fake submission we are using
expect(user.first_name).to.equal("Sean");
//remove the entry from the database
model.destroy({
where: {
id: user.id
}
}).then(function() {
done();
})
})
});
// update
it(`should update the ${model.modelName} entry in the database`, function () {
model.create(modelData).then(function (user) {
//after user is created, then update a value
modelData.guest_count = 12
model.update(modelData, {
where: {
id: user.id
}
}).then(function(data) {
model.findOne({
where: {
id: user.id
}
}).then(function (data) {
expect(data.guest_count).to.equal(12);
}).then(function () {
model.destroy({
where: {
id: user.id
}
}).then(function() {
done()
})
})
})
})
})
#mcranston18 left a really nice detailed accepted answer.
What I'd like to add for others that find the question or for OP in the future, is the use of async/await:
describe('some test block', function() {
it('should do something', async function() { // notice async and no done
const users = await User.findAll()
// expect users.to (...)
})
})
Here's a really simple way to create then update using async/await:
describe('some test block', function () {
it('should do something', async function () {
const Joe = await User.create({ name: 'Jo' }) // oops
// assertions/expect/should
// ex: expect(Joe.name).to.equal('Jo')
await Joe.update({ name: 'Joe' }) // that's better
// assertions/expect/should
// ex: expect(Joe.name).to.equal('Joe')
})
})
Related
I am writing unit tests for my NestJS application. However, I am getting into this error which I don't get how I can resolve. I am resolving the Promise as well.
My update function in service.ts looks like this:
async update(uname: string, updateUserDto: UpdateUserDTO): Promise<Users> {
const updateUser = await prisma.users.findUnique({
where: {
username: uname,
},
});
if (!updateUser || updateUser.username !== uname) {
throw new NotFoundException('User not found');
}
await prisma.tasks.updateMany({
where: {
userId: uname,
},
data: {
userId: uname,
},
});
return prisma.users.update({
where: {
username: uname,
},
data: {
...updateUserDto,
},
});
}
And my testing file looks like this.
controller.spec.ts:
describe('Update a user', () => {
it('should update the username of a user', async () => {
const updatedUser: UpdateUserDTO = {
name: 'Hulk',
};
await expect(
controller.update('wolverine', updatedUser)
).resolves.toEqual({
username: 'wolverine',
...updatedUser,
});
});
});
});
Update mock in controller.spec.ts is like this:
update: jest
.fn()
.mockImplementation((username: string, user: UpdateUserDTO) =>
Promise.resolve({ username, ...user })
),
And my controller where I am using update is, controller.spec.ts
#Patch(':uname')
update(#Param('uname') uname: string, #Body() updateUserDto: UpdateUserDTO) {
return this.usersService.update(uname, updateUserDto);
}
I'm trying to write a unit test for one of my Express controller functions, however, the sinon calls don't wait for the controller function to finish. Do I need to write the controller function as async so I can await in the test?
it('should allow creating', function (done) {
const req = {
body: {
name: 'something here',
isPrivate: 'true',
},
flash: sinon.spy(),
user: { _id: mongoose.Types.ObjectId() }
};
const res = {
redirect: sinon.spy()
}
leagueController.create(req, res);
req.flash.calledWith('success', 'League created').should.be.true;
res.redirect.calledWith('/leagues').should.be.true;
League.findOne({ name: 'something here' }, (err, league) => {
should.exist(league);
done();
});
});
Here is a simplified controller function:
function create(req, res) {
const { name, isPrivate } = req.body;
let newLeague = new League({
name: name,
isPrivate: isPrivate,
});
newLeague.save(function (err) {
if (err) {
req.flash('error', err.message || err);
debug(err.message);
return res.redirect('/leagues/new');
}
req.flash('success', 'League created');
debug('league created');
return res.redirect('/leagues');
});
}
If your controller isn't actually sending a network request (which I hope not in a unit test) , easiest way here would be to use fake-timers and do a small clock.tick(50).
If it actually does something asynchronous, then best way would be to return a promise from newLeague.save
So I've been stuck on this problem for the last week I'd say.
I'm trying to add a workout to my database and I'm receiving from my frontend 3 things: the workout name as a string, an array called exercises that contains objects for each exercise in that workout. And finally, an array called sets that is a 2d array, each array contains an object for each set
const exercises = [
{name: ''}
]
const sets = [
[
{
reps: '',
kg: ''
}
]
]
I'm using knex and I want to insert this data into my database. This is my code:
app.post('/newworkout', (req, res) => {
const {workoutName, exercises, sets} = req.body;
const loggedSets = [];
let haveRoutinesFinished = false;
let haveExercisesFinished = false;
PushRoutinesToDB = () => {
db('routines')
.returning('id')
.insert({
userid: user.id,
name: workoutName
})
.then(routineID => {
workoutInfo.routineID = routineID[0];
haveRoutinesFinished = true;
console.log('1')
return db('loggedroutines')
.insert({
userid: user.id,
routineid: workoutInfo.routineID,
routinedate: new Date()
})
})
}
PushExercisesToDB = () => {
exercises.map(exercise => {
console.log('2')
db('exercises')
.insert({
userid: user.id,
routineid: workoutInfo.routineID,
name: exercise.name
})
.returning('id')
.then(exerciseID => {
console.log('exercise id', exerciseID)
exerciseIDArray.push(exerciseID[0])
console.log("exerciseIDArray", exerciseIDArray)
haveExercisesFinished = true;
return db('loggedexercises')
.insert({
userid: user.id,
routineid: workoutInfo.routineID,
exerciseid: exerciseID[0]
})
})
})
}
PushSetsToDB = () => {
sets.map((setsArray, i) => {
setsArray.map(set => {
loggedSets.push({
userid: user.id,
routineid: workoutInfo.routineID,
exerciseid: exerciseIDArray[i],
reps: set.reps,
kg: set.kg
})
})
})
console.log(loggedSets)
db('loggedsets').insert(loggedSets)
}
PushRoutinesToDB()
console.log(haveRoutinesFinished)
if (haveRoutinesFinished === true) {
PushExercisesToDB()
}
if (haveExercisesFinished === true) {
PushSetsToDB()
}
})
The code itself works, but due to the first database call being asynchronous, the second one doesn't work and it depends on the first one. I tried to use callbacks to counter this issue but that just causes my code to not run. Any ideas?
Since your functions are Asynchronous the thread continues even if the first function has not yet completed.
Using async/await must solve your problem.
NOTE THE COMMENTS TO SEE CHANGES
app.post('/newworkout', async (req, res) => { //asynchronous Function
const {workoutName, exercises, sets} = req.body;
const loggedSets = [];
let haveRoutinesFinished = false;
let haveExercisesFinished = false;
PushRoutinesToDB = async () => { //asynchronous Function
db('routines')
.returning('id')
.insert({
userid: user.id,
name: workoutName
})
.then(routineID => {
workoutInfo.routineID = routineID[0];
haveRoutinesFinished = true;
console.log('1')
return db('loggedroutines')
.insert({
userid: user.id,
routineid: workoutInfo.routineID,
routinedate: new Date()
})
})
}
PushExercisesToDB = async () => { //asynchronous Function
exercises.map(exercise => {
console.log('2')
db('exercises')
.insert({
userid: user.id,
routineid: workoutInfo.routineID,
name: exercise.name
})
.returning('id')
.then(exerciseID => {
console.log('exercise id', exerciseID)
exerciseIDArray.push(exerciseID[0])
console.log("exerciseIDArray", exerciseIDArray)
haveExercisesFinished = true;
return db('loggedexercises')
.insert({
userid: user.id,
routineid: workoutInfo.routineID,
exerciseid: exerciseID[0]
})
})
})
}
PushSetsToDB = async () => { //asynchronous Function
sets.map((setsArray, i) => {
setsArray.map(set => {
loggedSets.push({
userid: user.id,
routineid: workoutInfo.routineID,
exerciseid: exerciseIDArray[i],
reps: set.reps,
kg: set.kg
})
})
})
console.log(loggedSets)
db('loggedsets').insert(loggedSets)
}
// Now your all functions are asynchronous You can call them in series with 'await'
await PushRoutinesToDB()
await PushExercisesToDB()
await PushSetsToDB()
})
I have wrote a simple Update function. Its working fine for some minutes and then again its not working. Where I am going wrong? Please help me. I use PUT as my method.
code
accept = (req, res) => {
this._model.update({
user: new mongoose.Types.ObjectId(req.params.uid)
}, {
$set: {
status: 'active'
}
}, (err, obj) => {
if (err || !obj) {
res.send(err);
} else {
res.send(obj);
}
});
}
Model
{
"_id":"5d3189a00789e24a23438a0d",
"status":"pending",
"user":ObjectId("5d3189a00789e24a23438a0d"),
"code":"CT-123-345-234-233-423344",
"created_Date":"2019-07-19T09:13:04.297Z",
"updated_Date":"2019-07-19T09:13:04.297Z",
"__v":0
}
Request
api.abc.com/api/accept/5d3189a00789e24a23438a0d
Sometime it is returing values and sometime null.
You can use the following code to ensure the model is tied to a connection. This could be an issue of connection to the database.
const config = require('./config');
console.log('config.database.url', config.database.url);
return mongoose.createConnection(config.database.url, {
useMongoClient: true
})
.then((connection) => {
// associate model with connection
User = connection.model('User', UserSchema);
const user = new User({
email: 'someuser#somedomain.com',
password: 'xxxxx'
});
const prom = user.update();
// Displays: 'promise: Promise { <pending> }'
console.log('promise:', prom);
return prom
.then((result) => {
// Don't see this output
console.log('result:', result);
})
.catch((error) => {
// Don't see this output either
console.log('error:', error);
});
})
.catch((error) => {
console.log(error);
});
I think you need to use promise or async/await, try this
accept = async (req, res) => {
try {
const result = await this._model.update({
user: new mongoose.Types.ObjectId(req.params.uid)
}, {
$set: {
status: 'active'
}
});
return res.send(result);
} catch (e) {
return res.send(e);
}
};
I am following this graphql tutorial, everything was going ok until I try to use dataloaders.
My server.js is:
const start = async () => {
const mongo = await connectMongo();
const buildOptions = async req => {
const user = await authenticate(req, mongo.Users);
return {
context: {
dataloaders: buildDataloaders(mongo),
mongo,
user
},
schema
};
};
app.use('/graphql', bodyParser.json(), graphqlExpress(buildOptions));
app.use(
'/graphiql',
graphiqlExpress({
endpointURL: '/graphql',
passHeader: `'Authorization': 'bearer token-name#email.com'`
})
);
app.use('/', expressStaticGzip('dist'));
app.use('/attendance', expressStaticGzip('dist'));
app.use('/login', expressStaticGzip('dist'));
spdy.createServer(sslOptions, app).listen(process.env.PORT || 8080, error => {
if (error) {
console.error(error);
return process.exit(1);
} else {
console.info(
`App available at https://localhost:${process.env.PORT || 3000}`
);
}
});
};
My copy and paste dataloaders.js:
const DataLoader = require('dataloader');
async function batchUsers(Users, keys) {
return await Users.find({ _id: { $in: keys } }).toArray();
}
module.exports = ({ Users }) => ({
userLoader: new DataLoader(keys => batchUsers(Users, keys), {
cacheKeyFn: key => key.toString()
})
});
And my resolvers.js:
export default {
Query: {
allLinks: async (root, data, { mongo: { Links } }) =>
Links.find({}).toArray()
},
Mutation: {
createLink: async (root, data, { mongo: { Links }, user }) => {
const newLink = Object.assign({ postedById: user && user._id }, data);
const response = await Links.insert(newLink);
return Object.assign({ id: response.insertedIds[0] }, newLink);
},
createUser: async (root, data, { mongo: { Users } }) => {
const newUser = {
name: data.name,
email: data.authProvider.email.email,
password: data.authProvider.email.password
};
const response = await Users.insert(newUser);
return Object.assign({ id: response.insertedIds[0] }, newUser);
},
signinUser: async (root, data, { mongo: { Users } }) => {
const user = await Users.findOne({ email: data.email.email });
if (data.email.password === user.password) {
return { token: `token-${user.email}`, user };
}
}
},
Link: {
id: root => root._id || root.id,
postedBy: async ({ postedById }, data, { dataloaders: { userLoader } }) => {
return await userLoader.load(postedById);
}
},
User: {
id: root => root._id || root.id
}
};
When I try get my allLinks I got the error:
TypeError: The loader.load() function must be called with a value,but
got: undefined.
Can anyone help me?
So I was able to reproduce the error by creating a link with a user, deleting the user from the Mongo database, and then querying for the postedBy attribute of the Link.
I would suggest dropping all your links and recreating your user (register + sign in), creating a new link, then querying for the postedBy field.