I keep getting an error in the save() method when I run the test.
var User = require('../../models/user')
, should = require('should');
describe('User', function(){
describe('#save()', function(){
it('should save without error', function(done){
var user = new User({
username : 'User1'
, email : 'user1#example.com'
, password : 'foo'
});
user.save(function(err, user){
if (err) throw err;
it('should have a username', function(done){
user.should.have.property('username', 'User1');
done();
});
});
})
})
})
here is the error:
$ mocha test/unit/user.js
․
✖ 1 of 1 test failed:
1) User #save() should save without error:
Error: timeout of 2000ms exceeded
at Object.<anonymous> (/usr/local/lib/node_modules/mocha/lib/runnable.js:1
61:14)
at Timer.list.ontimeout (timers.js:101:19)
You can nest describes but not it tests. Each test is meant to be stand alone so when you are looking through your results you can see where it is failing - on the save or not having the username property. For example in your code above there is no way for it to fail the 'should save without error' test as there is no done(). Which is also the reason the code above is timing out: mocha cannot find the done() for the 'should save without error' test.
Being able to nest describes is very powerful though. Within each describe you can have a before, beforeEach, after and afterEach statement. With these you can achieve the nesting that you are attempting above. See the mocha docs for more information if you want to read up on these statements.
The way I would write what you are trying to achieve is as follows:
var User = require('../../models/user')
, should = require('should')
// this allows you to access fakeUser from each test
, fakeUser;
describe('User', function(){
beforeEach(function(done){
fakeUser = {
username : 'User1'
, email : 'user1#example.com'
, password : 'foo'
}
// this cleans out your database before each test
User.remove(done);
});
describe('#save()', function(){
var user;
// you can use beforeEach in each nested describe
beforeEach(function(done){
user = new User(fakeUser);
done();
}
// you are testing for errors when checking for properties
// no need for explicit save test
it('should have username property', function(done){
user.save(function(err, user){
// dont do this: if (err) throw err; - use a test
should.not.exist(err);
user.should.have.property('username', 'User1');
done();
});
});
// now try a negative test
it('should not save if username is not present', function(done){
user.username = '';
user.save(function(err, user){
should.exist(err);
should.not.exist(user.username);
done();
});
});
});
});
Related
I'm trying to test a service in my Nest.js app. In some of my methods of this service I have some cases where I throw new HttpException depending of the context.
I'm trying to write unit tests of these methods and I don't know how to test cases where I throw HttpException.
I've tried this code:
it('Shound return error for a non existing id provided', async () => {
await expect(service.getUser('false Id')).rejects.toThrow(new HttpException('This user does not exist', 404));
});
But I received:
Expected the function to throw an error matching:
[Error: This user does not exist]
Instead, it threw:
Error: This user does not exist
Does anyone already encounter this use case ?
Duc answer works but if you want some prettier way you might like this,
it('should return null when update', async () => {
await expect(controller.updateById({ id: 'some id' }, {}))
.rejects.toThrowError(NotFoundException);
});
Simply do this:
it('should return null when update', async (done) => {
jest.spyOn(service, 'updateById').mockResolvedValue(null)
try {
await controller.updateById({ id: 'some id' }, {})
} catch (error) {
expect(error).toBeInstanceOf(NotFoundException)
done()
}
})
I have a backend server using Node and Express.js and I'm using Jest as my test framework. I had a problem that I couldn't figure out. Let me share my code first:
// user.model.test.ts
describe('User model test', () => {
let connection: Connection;
beforeEach(async () => {
connection = await createConnection(ormConfig);
});
afterEach(async () => {
connection.close();
});
it('should not create a user with the same email', async () => {
await User.create({
username: 'bar',
email: 'foo#gmail.com',
password: 'password'
}).save();
const user2 = User.create({
username: 'foo',
email: 'foo#gmail.com',
password: 'password'
});
await expect(user2.save()).rejects.toThrowError();
});
}
As you can see here I'm creating a separate connection from my development database here the hooks are attached to test_db.
I have another test on controllers.
user.controller.test.ts
describe('Get all users', () => {
let connection: Connection;
beforeEach(async () => {
connection = await createConnection(ormConfig);
});
afterEach(async () => {
connection.close();
});
it('should return status code 200', async (done) => {
User.create({
username: 'test',
email: 'foobar#gmail.com',
password: 'password'
});
const res = await rekwest.get('/api/v1/users');
expect(res.status).toBe(200);
// console.log(res.st);
done();
});
});
Same thing what I'm doing here. I'm creating a test and attaching to test_db and expecting a different connection pool.
Now the error on the controller is weird User table already exists. And on the model I have an error of test_db doesn't exists. Now if I removed the hooks in the controller test the errors goes away but I can't test the controller.
So I found a link on how to test a MongoDB using Jest we have the same idea but I don't have the runInBand. So I added it jest --runInBand and for some reason, it fixes my errors. Can someone explain to me what happened? In the Jest documentation basically it's saying that your test will run 50% faster but I don't need that or do I?
I'm trying to test a service that I've created that makes an API call with vue-resource. The service should make the call and update the component with the new data, however in my tests it doesn't register the updated value. I'm using the same setup as the vue-cli webpack example and have based my auth service off this repo (which unfortunately doesn't have any tests)
my service:
export default {
login(context, creds){
context.$http.post(LOGIN_URL, creds)
.then((response) => {
//do something else here
}, (response) => {
context.error = response.data.error
}
}
}
my test:
import Vue from 'vue'
import VueResource from 'vue-resource'
Vue.use(VueResource)
import Auth from 'src/auth'
describe('Auth', () => {
it('should throw an error on unsuccessful login', (done) => {
//intercept the api call and force an invalid response
Vue.http.interceptors.unshift((request, next) => {
next(request.respondWidth({error: 'some error'}, {status: 401}));
});
const vm = new Vue({
data: {
error: ''
}
}).$mount()
Auth.login(vm, {email: 'test#test.com', pass: 'test'} )
//this always fails
expect(vm.error).to.equal('some error')
//ive also tried:
vm.$nextTick(() => {
expect(vm.error).to.equal('some error')
//done()
});
//undo our interceptor
Vue.http.interceptors.shift()
}
}
When I run the test it fails because it's expecting '' to equal 'some error'.
My suspicions are around the fact that vue-resource is using promises.
After reading through some of the Vue.js tests I found my answer. Instead of using vm.$nextTick I did the following:
setTimeout(function(){
expect(vm.error).to.equal('something')
done()
}, 0)
I'm having a hard time figuring this out. I'm starting to build my testscript. Even though its a little late. But unit testing is a good practice. For a start I just want to test two scope model (scope.global & scope.security) but I got a weird error saying my MainCtrl is not a function?
'use strict';
/* Controllers */
angular.module('mainCtrl', ['LocalStorageModule'])
.controller('MainCtrl', ['$scope' ,'$rootScope', '$location', 'Api', 'Security', 'Utils', 'localStorageService',function (scope, rootScope, location, Api, Security, Utils, session) {
console.log('main js loaded');
// $scope.main = {};
scope.global = {};
scope.security = Security;
....
}]);
My controllerSpec.js
describe('controllers MainCtrl', function(){
beforeEach(function(){
module('Services.api'),
module('LocalStorageModule')
});
describe('MainCtrl', function() {
var scope, api, security, session;
beforeEach(inject(function($rootScope, $controller ,$location, Api, localStorageService){
scope = $rootScope.$new();
$location = $location;
$controller("MainCtrl", {
$scope : scope,
localStorageService : localStorageService,
Api : Api
});
}));
it('should create "usertype" model with empty list', function(){
expect(scope.global).toBe(undefined);
});
});
});
Error result from above code:
Chrome 24.0 (Linux) controllers MainCtrl MainCtrl should create "usertype" model with empty list FAILED
Error: Argument 'MainCtrl' is not a function, got undefined
I've tested my webapp on a browser and it doesn't encounter this MainCtrl is not function.
Please help.
Is this correct?
angular.module('mainCtrl', ['LocalStorageModule'])
It seems like the module should be named something other than mainCtrl since that is what your controller is named.
In your test, you are not loading the module:
beforeEach(function(){
module('Services.api'),
module('LocalStorageModule')
});
Does anyone know how to test Mongoose Validations?
Example, I have the following Schema (as an example):
var UserAccount = new Schema({
user_name : { type: String, required: true, lowercase: true, trim: true, index: { unique: true }, validate: [ validateEmail, "Email is not a valid email."] },
password : { type: String, required: true },
date_created : { type: Date, required: true, default: Date.now }
});
The validateEmail method is defined as such:
// Email Validator
function validateEmail (val) {
return /^[a-zA-Z0-9._-]+#[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}$/.test(val);
}
I want to test the validations. The end result is that I want to be able to test the validations and depending on those things happening I can then write other tests which test the interactions between those pieces of code. Example: User attempts to sign up with the same username as one that is taken (email already in use). I need a test that I can actually intercept or see that the validation is working WITHOUT hitting the DB. I do NOT want to hit Mongo during these tests. These should be UNIT tests NOT integration tests. :)
Thanks!
I had the same problem recently.
First off I would recommend testing the validators on their own. Just move them to a separate file and export the validation functions that you have.
This easily allows your models to be split into separate files because you can share these validators across different models.
Here is an example of testing the validators on their own:
// validators.js
exports.validatePresenceOf = function(value){ ... }
exports.validateEmail = function(value){ ... }
Here is a sample test for this (using mocha+should):
// validators.tests.js
var validator = require('./validators')
// Example test
describe("validateEmail", function(){
it("should return false when invalid email", function(){
validator.validateEmail("asdsa").should.equal(false)
})
})
Now for the harder part :)
To test your models being valid without accessing the database there is a validate function that can be called directly on your model.
Here is an example of how I currently do it:
describe("validating user", function(){
it("should have errors when email is invalid", function(){
var user = new User();
user.email = "bad email!!"
user.validate(function(err){
err.errors.email.type.should.equal("Email is invalid")
})
})
it("should have no errors when email is valid", function(){
var user = new User();
user.email = "test123#email.com"
user.validate(function(err){
assert.equal(err, null)
})
})
})
The validator callback gets an error object back that looks something like this:
{ message: 'Validation failed',
name: 'ValidationError',
errors:
{ email:
{ message: 'Validator "Email is invalid" failed for path email',
name: 'ValidatorError',
path: 'email',
type: 'Email is invalid'
}
}
}
I'm still new to nodeJS and mongoose but this is how I'm testing my models + validators and it seems to be working out pretty well so far.
You should use validate() method as a promise and test it with a tool that makes asserts for async stuff (ex: Chai as Promised).
First of all, require a promise library and switch out to the promise provider (for example Q):
mongoose.Promise = require('q').Promise;
Afterwards just, use asserts about promises:
it('should show errors on wrong email', function() {
user = new UserModel({
email: 'wrong email adress'
});
return expect(user.validate()).to.be.rejected;
});
it('should not show errors on valid email adress', function() {
user = new UserModel({
email: 'test#test.io'
});
return expect(user.validate()).to.be.fulfilled;
});