I want to mock two related tables with data to be able to test a function
var User = dbMock.define('user', {
id:1,
username: 'myTestUsername',
email: 'test#example.com',
});
var UserTeam = dbMock.define('team', {
idTeam: 1,
idUser: 1,
});
var Team = dbMock.define('team', {
name: 'Test Team',
});
and this worked for me when I only have one table, but I am not sure how to export with Jest when I want to mock multiple tables
jest.mock('../sequelize/models/users', () => () => {
const SequelizeMock = require("sequelize-mock");
const dbMock = new SequelizeMock();
return dbMock.define('users', {
id: 1,
username: 'myTestUsername',
email: 'test#example.com',
})
});
The example below, from https://sequelize-mock.readthedocs.io/, has a example:
// Define our Model
var UserMock = DBConnectionMock.define('users', {
'email': 'email#example.com',
'username': 'blink',
'picture': 'user-picture.jpg',
}, {
instanceMethods: {
myTestFunc: function () {
return 'Test User';
},
},
});
// You can also associate mock models as well
var GroupMock = DBConnectionMock.define('groups', {
'name': 'My Awesome Group',
});
UserMock.belongsTo(GroupMock);
Related
This is also a question on Auth0 node-auth0 library. The use case is that I am using the Auth0 create Actions API through Terraform and want to be able to writes tests against the Actions.
In this example I want to be able to test the onExecutePostLogin without using real values.
// auth0-post-login.js
exports.onExecutePostLogin = async (event, api) => {
const userId = event.user.user_id
const ManagementClient = require('auth0').ManagementClient
const management = new ManagementClient({
domain: event.secrets.domain,
clientId: event.secrets.clientId,
clientSecret: event.secrets.clientSecret,
audience: `https://${event.secrets.domain}/api/v2/`,
scope: 'read:users',
})
const params = { id: userId, page: 0, per_page: 50, include_totals: false }
let userPermissions = await management.getUserPermissions(params)
const map = require('array-map')
const permissions = map(userPermissions, function(permission) {
return permission.permission_name
})
api.accessToken.setCustomClaim('https://example.com/access', permissions.join(' '))
}
One of the main issues is that the functions like getUserPermissions is created through their utility wrapper:
utils.wrapPropertyMethod(ManagementClient, 'getUserPermissions', 'users.getPermissions');
This causes jest to have issues finding the functions.
I did something similar as stsmurf to mock the response of the Auth0 methods.
I have a file where I store my helper methods like "find a role by its name"
// helper.ts
import { ManagementClient } from 'auth0';
export const getRoleByName = async (roleName: string) => {
const api = new ManagementClient({
clientId: clientId,
clientSecret: clientSecret,
domain: domain,
});
const roles = await api.getRoles();
const role = roles.find((r) => r.name == roleName);
if (!role) throw new Error('Role not found');
return role;
};
// helper.test.ts
import { Role } from 'auth0';
import { getRoleByName } from './helpers';
const mockGetRoles = jest.fn();
jest.mock('auth0', () => {
return {
ManagementClient: jest.fn().mockImplementation(() => {
return {
getRoles: mockGetRoles,
};
}),
};
});
describe('Get role', () => {
beforeAll(() => {
const dummyRoles: Role[] = [
{ id: 'fake_id_1', description: 'Fake role nr 1', name: 'Fake Role 1' },
{ id: 'fake_id_2', description: 'Fake role nr 2', name: 'Fake Role 2' },
];
mockGetRoles.mockImplementation(() => dummyRoles);
});
it('can return a role if it exists', async () => {
const expectedResult: Role = {
id: 'fake_id_1',
description: 'Fake role nr 1',
name: 'Fake Role 1',
};
const result = await getRoleByName('Fake Role 1');
expect(expectedResult).toEqual(result);
});
it('will throw an error when a role is not found', async () => {
await expect(getRoleByName('Fake Role 3')).rejects.toThrowError();
});
});
In order to get to functions like getUserPermissions I needed to override the implementation of the ManagementClient. This allowed me to then define getUserPermissions as a mock function.
import { onExecutePostLogin } from './auth0-post-login'
const mockManagementClient = jest.fn()
const mockGetUserPermissions = jest.fn()
jest.mock('auth0', () => {
return {
ManagementClient: (opts) => {
mockManagementClient(opts)
return {
getUserPermissions: (params) => {
return mockGetUserPermissions(params)
},
}
},
}
})
describe('onExecutePostLogin', () => {
const mockSetCustomClaim = jest.fn()
beforeEach(() => {
mockSetCustomClaim.mockClear()
mockManagementClient.mockClear()
mockGetUserPermissions.mockClear()
mockGetUserPermissions.mockReturnValue([
{
permission_name: 'read:foo',
},
{
permission_name: 'update:bar',
},
])
})
const event = {
user: {
user_id: 'abcd123',
},
secrets: {
domain: 'test-example.us.auth0.com',
clientId: 'a-client-id',
clientSecret: 'a-client-secret',
},
}
const api = {
accessToken: {
setCustomClaim: mockSetCustomClaim,
},
}
it('initializes a ManagementClient', async () => {
await onExecutePostLogin(event, api)
expect(mockManagementClient).toHaveBeenCalledWith({
domain: event.secrets.domain,
clientId: event.secrets.clientId,
clientSecret: event.secrets.clientSecret,
audience: `https://${event.secrets.domain}/api/v2/`,
scope: 'read:users',
})
})
it('gets users permissions', async () => {
await onExecutePostLogin(event, api)
expect(mockGetUserPermissions).toHaveBeenCalledWith(
{ id: event.user.user_id, page: 0, per_page: 50, include_totals: false },
)
})
it('sets custom claims', async () => {
await onExecutePostLogin(event, api)
const expectedPermissions = 'read:foo update:bar'
expect(mockSetCustomClaim).toHaveBeenCalledWith(
'https://example.com/access', expectedPermissions,
)
})
})
I am trying to unit test my code, I am using Node.js, Express, MongoDB, Mongoose and trying to test it with Jest.
I am having issues with the in-memory mongo db with Jest. I have read the documentation (https://jestjs.io/docs/en/mongodb).
When trying to test a query for one of my Mongoose models, I believe that my query is not pointing at the correct place, I would like it to run its query against the in-memory collection.
My current test setup:
const { MongoClient } = require("mongodb");
const Preferences = require("./preferences");
const mongoose = require("mongoose");
const ObjectId = mongoose.Types.ObjectId;
describe("Preference helper function", () => {
let connection;
let db;
let userPreferences;
const testUser = new Preferences({
userId: ObjectId("5eef15429e93464b3ccae235"),
wordPreferences: [
{ wordId: 1, active: true},
{ wordId: 2, active: true},
{ wordId: 3, active: true},
{ wordId: 4, active: true},
{ wordId: 5, active: true}
],
});
beforeAll(async () => {
connection = await MongoClient.connect(global.__MONGO_URI__, {
useNewUrlParser: true,
});
db = await connection.db(global.__MONGO_DB_NAME__);
});
afterAll(async () => {
await connection.close();
await db.close();
});
beforeEach(async () => {
userPreferences = await db.collection("userPreferences");
});
afterEach(async () => {
await userPreferences.deleteMany();
});
it("get active words function gives correct result", async () => {
await userPreferences.insertOne(testUser);
const expectedResult = [1, 2, 3, 4, 5];
let queryResult = await Preferences.getActiveWordIds(testUser.userId);
expect(queryResult).toEqual(expectedResult);
});
});
Running this code gives the following error:
Timeout - Async callback was not invoked within the 5000 ms timeout specified by jest.setTimeout.Timeout - Async callback was not invoked within the 5000 ms timeout specified by jest.setTimeout.Error
Changing the timeout for this does not resolve the problem as it appears the query does not resolve. Here is the the Moongoose model for the Preference model used in the test above:
const mongoose = require("mongoose");
const PreferenceMappingSchema = require("../preference-mapping");
var PreferenceSchema = new mongoose.Schema({
userId: {
type: mongoose.Types.ObjectId,
required: true,
},
wordPreferences: {
type: [PreferenceMappingSchema],
required: true,
},
});
// Choose the correct db and set the model
const db = mongoose.connection.useDb("users");
const Preferences = (module.exports = db.model("Preference", PreferenceSchema));
module.exports.getActiveWordIds = async function(userId) {
try {
const user = await Preferences.aggregate([
// Get the current users doc
{ $match: { userId: userId} },
{
$project: {
wordPreferences: {
// Get only active words
$filter: {
input: "$wordPreferences",
as: "pref",
cond: {
$eq: ["$$pref.active", true],
},
},
},
},
}
]).exec();
// Map to list of valid numbers
return user[0]['wordPreferences'].map(x => x.wordId);
} catch (error) {
console.warn(error);
throw error;
}
};
Thanks in advance for any help!
I am using mocha and chai for writing test for RESTful APIs
I have read some articles where people suggests to create stubs for queries, and you shouldn't be actually making a database query.
But How would I make sure if it works?
See below controller.
const Op = require('sequelize').Op
//Models
const {
Item,
Location,
Combo,
Service,
ComboItem,
ItemLocation
} = require('../models')
const _ = require('lodash')
//Services
const paginate = require('../services/PaginationService')
const getAllItems = async function(req, res) {
if(req.query.location_id){
let items
const item = await Location.findOne({
where: {
id: 1
},
include: {
model: Item,
through: {
model: ItemLocation,
attributes: []
},
as: 'itemsAtLocation',
include: [
{
model: Service,
as: 'service',
attributes: ["id"]
},
{
model: Combo,
as: 'combo',
attributes: ["start_date", "expiry_date"]
}
]
}
})
if(!item)
return res.status(200).send({
status: true,
message: "No item found at location!",
data: {}
})
items = item.itemsAtLocation
let data = {}
data.services = []
data.combos = []
_.forEach(items, item => {
let itemData = {
id: item.id,
name: item.name,
price: item.price,
discount_per: item.discount_per,
}
if(item.service)
data.services.push(itemData)
if(item.combo) {
itemData.start_date = item.combo.start_date
itemData.expiry_date = item.combo.expiry_date
data.combos.push(itemData)
}
})
return res.status(200).send({
status: true,
message: "Successfully fetch all items!",
data: data
})
} else {
const items = await Item.findAll({
include: [
{
model: Service,
as: 'service',
attributes: ["id"]
},
{
model: Combo,
as: 'combo',
attributes: ["start_date", "expiry_date"]
}
],
attributes: ["id", "name", "price", "discount_per", "description"],
...paginate(+req.query.page, +req.query.per_page)
})
let data = {}
data.services = []
data.combos = []
_.forEach(items, item => {
let itemData = {
id: item.id,
name: item.name,
price: item.price,
discount_per: item.discount_per,
}
if(item.service)
data.services.push(itemData)
if(item.combo) {
itemData.start_date = item.combo.start_date
itemData.expiry_date = item.combo.expiry_date
data.combos.push(itemData)
}
})
return res.status(200).send({
status: true,
message: "Successfully fetch all items!",
data: data
})
}
}
module.exports = {
getAllItems
}
As you can see from above code. I need queries to return data in a specific form. If it won't be in that form things won't work.
Can someone suggest how can I create stubs for such kind of functions so that structure also be preserved?
Below is the test that I have wrote, But it uses actual db calls.
describe('GET /api/v1/items', function () {
it('should fetch all items orgianized by their type', async () => {
const result = await request(app)
.get('/api/v1/items')
.set('Accept', 'application/json')
.expect('Content-Type', /json/)
.expect(200)
expect(result)
.to.be.a('Object')
expect(result.body.status)
.to.be.a('Boolean').true
expect(result.body.data, "data should be an Object and every key should an Array")
.to.satisfy(data => {
expect(data).to.be.a('Object')
.to.not.be.null
if(!_.isEmpty(data)) {
expect(data).to.have.any.keys('services', 'combos')
_.forOwn(data, (value, key) => {
expect(data[key]).to.be.a('Array')
})
return true
}
return true
})
})
})
One way you can do that is by stubbing the methods from your models, i.e. Location.findOne and Item.findAll. So your tests could look a bit like the code below:
const sinon = require('sinon');
const Location = require('../models/location'); // Get your location model
const Item = require('../models/item'); // Get your item model
describe('myTest', () => {
let findOneLocationStub;
let findAllItemsStub;
beforeEach(() => {
findOneLocationStub = sinon.stub(Location, 'findOne');
findAllItemsStub = sinon.stub(Item, 'findAll');
});
afterEach(() => {
findOneLocationStub.verifyAndRestore();
findAllItemsStub.verifyAndRestore();
});
it('returns 200 when location not found', () => {
findOneLocationStub.resolves(null);
expects...
});
});
I did not run the test, but something like that should work. But note that I had to split the models into their own file to do the stub. Probably there's a way to do the same using your current implementation.
Another thing I would suggest is having some kind of use case into your method that is responsible for database implementation. Something like:
const getAllItemsUseCase = (params, queryService) => {
if(params.locationId){
let items
const item = await queryService.findOneLocation({
};
So when you call this method from your controller, you can do call:
const getAllItems = async function(req, res) {
const params = {
locationId: req.query.location_id,
// and more parameters
};
const queryService = {
findOneLocation: Location.findOne,
};
const results = await getAllItemsUseCase(params, queryService);
}
This way you will detach your business logic from the controller and you will have a much easier time to mock your query: you just change the methods provided to queryService.
You can find some interesting read from this blog post: https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html
I am trying to create a collection in mongodb where a field named lists will contain an array of link and linkName. I am successfully able to create a two seperate field link and linkName, however not able to store the value inside lists.
Model code for mongodb :-
const socialSchema = new Schema({
lists: [{
link:{ formType: String},
linkName: { formType: String}
}]
})
API code :-(this code is for creating only, will later on try to use findOneAndUpdate to update the existing field
router.route('/', [auth]).post(async (req, res) => {
const {linkName, link } = req.body
try {
console.log(req.body)//Ex. { linkName: 'facebook', link: 'www.facebook.com'}
const social = new Social({
//Stuck here!!!
})
await social.save()
res.json(social)
} catch (err) {
console.error(err.message);
res.status(500).send('Server Errors')
}
}
)
Part of frontend Code(React)
const [formData, setFormData] = useState({
linkName: '',
link: ''
});
const {linkName, link} = formData
const onChange = e =>
setFormData({ ...formData, [e.target.name]: e.target.value });
const handleSubmit = async e => {
e.preventDefault()
const socialList = {
linkName,
link
}
try {
const config = {
headers: {
'Content-Type': 'application/json'
}
};
const body = JSON.stringify(socialList)
const res = await Axios.post('/api/social', body, config)
console.log(res)
} catch (err) {
console.error(err);
}
}
In your schema change from {formType: String} to {type: String}.
const data = {link: req.body.link, linkName: req.body.linkName};
Social.create({
links: [data]
});
This should work.
MY FULL WORKING CODE THAT I TESTED
const schema = new mongoose.Schema({
links: [
{
link: { type: String },
linkName: { type: String }
}
]
});
const Model = mongoose.model("test", schema);
const doc = { link: "link", linkName: "linkname" };
Model.create({
links: [doc]
});
I am new to Node js. Below code I have written.It does not show any output.
const pdClient = require('node-pagerduty');
const pdApiKey = 'XXXXXXXXXXXXXX';
const pd = new pdClient(pdApiKey);
let from = 'XXXXXXXXXX#XXX.com';
let payload = {
user: {
type: 'user',
name: 'test',
email: 'test#gmail.com',
role: 'Manager'
}
};
var res = pd.users.createUser(from, payload);
console.log(res);
I believe the node-pagerduty client is promise based. Therefore the API functions return a Promise that you have to call .then() on or use await.
If you try:
pd.users.createUser(from, payload).then((result) => {
console.log('Result: ', result);
}).catch((error) => {
console.log("Error: ", error);
});
You should get a result!
If you wish to use the async / await pattern you can try:
async function testCreateUser() {
const pdClient = require('node-pagerduty');
const pdApiKey = 'XXXXXXXXXXXXXX';
const pd = new pdClient(pdApiKey);
let from = 'XXXXXXXXXX#XXX.com';
let payload = {
user: {
type: 'user',
name: 'test',
email: 'test#gmail.com',
role: 'Manager'
}
};
var res = await pd.users.createUser(from, payload);
console.log(res);
}
testCreateUser();