How to write Jasmine unit tests for this block of code - javascript

I need help understanding how to design test for this piece of code:
onSuccessHandler(response) {
if (response && this.cartModel.hasData(response)) {
const data = this.cartModel.parse(response);
this.cartModel.set(data);
cartChannel.request('render:cart');
}
}
I tried this, to see if request was called on cart channel, it seems to be passing, but code coverage still shows the block above is not covered :).
describe('Cart suite', () => {
let view;
const cartChannel = Radio.channel('cart');
beforeEach(() => {
view = new CartWidgetView({
model: new Model(fixturesObject),
cartModel,
});
view.render();
});
it('should request cart if response has data', () => {
//spyOn Channel in beforeEach method
view.cartModel = {
hasData: () => true,
parse: () => true,
set: (data) => {
view.data = data;
},
};
view.onButtonClick();
view.render();
const response = {};
cartChannel.trigger('render:cart', cartModel);
view.onSuccessHandler(response);
expect(cartChannel.request).toHaveBeenCalled();
expect(cartChannel.request).toHaveBeenCalledWith('get:model');
});
});
I'm new to TDD using Jasmine with MarionetteJS. The goal is to get it covered by test coverage.

Related

How to fix Cannot read property ‘$api’ of undefined in vue-test-utils

I have a button and will make an API call in the onclick function. The api .js is under the /api. It works well when I manually test it. However, I failed the unit test of the component because somehow it cannot find the api. I'm pretty new to the vuex test and not sure what is the correct way to mock the api call here.
Here is the click function in the component
methods: {
...mapActions([
'setNewField',
]),
async updateFields () {
const newField = {
value: this.fieldName,
text: this.fieldName,
}
await this.setNewField(newField)
}
}
Here is the related actions part in vue store. It calls the api and update the state
export const actions = {
async setNewField ({ commit }, field) {
await this.$app.$api.putField(field.value)
commit('SET_NEW_FIELD', field)
}
}
Here is the tester
describe('Selector', () => {
let store
let wrapper
let $app
const setNewFieldFunction = jest.fn()
beforeEach(() => {
jest.resetAllMocks()
$app = {
$api : {
putField: jest.fn()
}
}
store = new Vuex.Store({
modules: {
options: cloneDeep(optionsModule),
headers: {
actions: {
setNewField: setNewFieldFunction,
}
}
}
})
wrapper = shallowMount(Selector, { store, mocks: { $app } })
})
it('should add a field', async () => {
// add a field
expect(addButton.vm.disabled).toEqual(true)
fieldName.vm.$emit('input', 'TEST Field 1')
expect(addButton.vm.disabled).toEqual(false)
addButton.vm.$emit('click')
treeview.vm.$emit('input', ['TEST Field 1'])
await flushPromises()
expect(wrapper.vm.fieldName).toEqual('TEST Field 1')
expect(setNewFieldFunction).toHaveBeenCalled()
})
})
Thanks for any help!!

Problem with testing async functions in Angular with Jasmine

I am unit testing my Ionic 4 app with Jasmine. At the moment I am getting errors when running almost all my tests because I am doing something wrong with the async/await functions. The error that I get is: "Error: Timeout - Async function did not complete within 5000ms." I have changed this timeout_interval to another larger number and I still get this error.
My code is:
beforeEach(async(() => {
const storage = new Storage({
// Define Storage
name: '__mydb',
driverOrder: ['indexeddb', 'sqlite', 'websql']
});
component = new HomepagePage(storage);
TestBed.configureTestingModule({
declarations: [ HomepagePage ],
imports: [
IonicModule.forRoot(),
RouterTestingModule,
IonicStorageModule.forRoot()
]
}).compileComponents();
fixture = TestBed.createComponent(HomepagePage);
component = fixture.componentInstance;
fixture.detectChanges();
}));
it('should create', async () => {
let home = jasmine.createSpyObj('home_spy',['getprice'])
const result = await home.getprice
expect(component).toBeTruthy();
});
describe('before logged in', () => {
let home = jasmine.createSpyObj('home_spy',['getprice'])
it('shows the price', async () => {
const result = await home.getprice
expect(home.getprice.length).toEqual(2);
});
});
My app is working fine when I am using it. However, could it be that the error is in the code itself? An example of the getprice() function is:
async getprice() {
var price_eth :number
var price_btc :number
try {
var url_btc = "https://api.binance.com/api/v1/klines?symbol=BTCUSDT&interval=1m";
var url_eth = "https://api.binance.com/api/v1/klines?symbol=ETHUSDT&interval=1m"
const btc = await axios.get(url_btc)
const eth = await axios.get(url_eth)
if (btc.data.length != 0 && eth.data.length != 0) {
this.loaded = 'yes'
} else {
this.loaded='no'
}
this.time = new Date(btc.data[btc.data.length-1][0]).toTimeString().replace(/.*(\d{2}:\d{2}:\d{2}).*/, "$1");
price_btc = Math.floor(btc.data[btc.data.length-1][1])
price_eth = Math.floor(eth.data[eth.data.length-1][1])
//These global variables are declared so they display the price but they are not used for any other functions.
this.glob_price_btc = price_btc
this.glob_price_eth = price_eth
return {
'price_btc':price_btc,'price_eth':price_eth
}
} catch {
this.loaded = 'no';
return
}
}
I think you misunderstood the function beforeEach(). beforeEach() should be used to setup the test environment and will be called before an unittest will be exeuted.
Here is an example how it would be more correct and to setup the timeout duration.
describe("xyz test suite", () => {
//called before every unittest
beforeEach(function() {
//Do setup stuff
});
it('shows the price', async () => {
const result = await getSomething()
expect(result).toEqual(2);
});
}, 10000 <- Timeout in milliseconds)

Mock call in Typescript in unit test using only Mocha

I have the following method:
import { ObjectDal } from "./ObjectDal";
export class ObjectBL {
async getObject(id) {
try {
let dal = new ObjectDal();
let result = await dal.get(id);
return result;
} catch (err) {
// log the error
}
}
where the ObjectDal class is:
export class ObjectDal {
async get(id) {
// open connection to db
// make a query based on id
// put the result in a `result` variable
return result;
}
}
I have to write an unit test for the getObject() method using only Mocha...
This is the begining of the UT:
const assert = require('assert');
const ObjectBL = require("../ObjectBL");
describe('Something', () => {
describe('...', () => {
it('getObject_GetsObjectUsingID_True', async () => {
// arange
let id = "123456789101";
let expected = {
"name": "ana",
"hasApples": true
};
let test = new ObjectBL.ObjectBL();
let result = await test.getObject(id);
assert.deepStrictEqual(result, expected);
});
});
});
But in this case I would have to call the method from the ObjectDal class...
How can I mock the call to the get() method using only Mocha?
I found answers with Sinon, or Mocha with Sinon and/or Chai... but nothing with only Mocha...
Proxies might be the way to go for you.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy
You could mok methods by using a Proxy like so:
const assert = require('assert');
const ObjectBL = require("../ObjectBL");
describe('Something', () => {
describe('...', () => {
it('getObject_GetsObjectUsingID_True', async () => {
// arange
let id = "123456789101";
let expected = {
"name": "ana",
"hasApples": true
};
let test = new ObjectBL.ObjectBL();
const handler = {
get: function(obj, prop) {
// mok the getObject method
if(prop === 'getObject'){
return () => {
return Promise.resolve({
"name": "ana",
"hasApples": true
});
}
} else {
return obj[prop];
}
}
};
const p = new Proxy(test, handler);
let result = await p.getObject(id);
assert.deepStrictEqual(result, expected);
});
});
});
If you ONLY want to mok the ObjectDal.get method, you might want to override the prototype and recover it afterwards:
const assert = require('assert');
const ObjectBL = require("../ObjectBL");
const ObjectDal = require("../ObjectDal");
describe('Something', () => {
describe('...', () => {
it('getObject_GetsObjectUsingID_True', async () => {
// arange
let id = "123456789101";
let expected = {
"name": "ana",
"hasApples": true,
};
const proto = Object.getOwnPropertyDescriptor(ObjectDal, 'prototype').value;
const backup = proto.get;
proto.get = () => {
return Promise.resolve({
"name": "ana",
"hasApples": true,
});
}
let test = new ObjectBL.ObjectBL();
let result = await test.getObject(id);
ObjectDal.prototype.get = backup;
assert.deepStrictEqual(result, expected);
});
});
});
You could also override the ObjectDal with a Proxy and implement the construct handler to return a dummy ObjectDal, but this might be more tricky, since you are working with modules.
Testing is feedback, not just on whether or not your code works as advertised but even more crucially on the quality of your design.
The fact that you are having trouble writing the tests is your first sign you did something sub-optimal in the implementation. What you want is this:
export class ObjectBL {
constructor (dal) {
this.dal = dal;
}
async getObject(id) {
try {
let result = await this.dal.get(id);
return result;
} catch (err) {
// log the error
}
}
...and now the dependency is clear rather than implicit and will show up in editor tooltips, is more amenable to static analysis, etc. And it solves your problem: now you can mock it easily for testing, no further libraries needed.

How to mock request to service with database?

I'm new in Jest and don't understand how to mock request to service for unit test.
EmployeeController.js
const EmployeeService = require('../services/employeeService');
exports.getEmployeeById = (req, res) => {
EmployeeService.find(req.params.employeeId) // need to be mocked
.then((employee) => {
if (employee == 0 || '') {
return res.status(404).json({
success: false,
message: 'Employee not found!'
});
} else {
return res.status(200).json({
success: true,
employee: employee
});
}
}).catch(err => {
res.status(404).json({
success: false,
message: 'Employee not found!'
});
});
}
EmployeeService.find - returns to me from the database the employee object by the entered Id in url.
EmployeeService.js
const sql = require('../config/connection');
const Promise = require('bluebird');
const connection = require('../config/connection');
const Employee = require('../models/employee.model');
var queryAsync = Promise.promisify(connection.query.bind(connection));
Employee.find = async function (employeeId) {
var result = queryAsync(
"SELECT empID, empName, IF(empActive, 'Yes', 'No') empActive, dpName FROM Employee INNER JOIN Department ON empDepartment = dpID WHERE empID = ? ", employeeId);
return result;
}
employee.model.js - model of employee.
const Employee = function (emp) {
this.empName = emp.empName;
this.empActive = emp.empActive;
this.empDepartment = emp.empDepartment;
this.creator = emp.creator;
};
module.exports = Employee;
Jest has built in utilities for stubbing out dependencies.
const employeeService = require("../services/employeeService");
const employeeController = require("./employeeController");
describe("employeeController", () => {
beforeEach(() => {
// this mock can be overridden wherever necessary, eg by using
// employeeService.find.mockRejectedValue(new Error("oh no"));
// by default, resolve with something that meets the needs of your consuming
// code and fits the contract of the stubbed function
jest.spyOn(employeeService, "find").mockResolvedValue(someMockQueryResult);
});
afterEach(() => {
jest.restoreAllMocks();
});
// contrived test to show how jest stubs can be used
it("fetches the employee", async () => {
await employeeController.getEmployeeById(123);
expect(employeeService.find).toHaveBeenCalledWith(123);
});
});
There are also options for mocking out the entire module. Check out the jest docs:
https://jestjs.io/docs/en/mock-functions.html
https://jestjs.io/docs/en/manual-mocks.html

How can I mock a fake database for when unit testing against Knex?

I've been using Knex successfully to connect to a backend database. But I want to be able to unit test my code. Is there a way to mock the database connection?
I've tried using proxyquire but I can't seem to get it to work.
The problem seems to be with the way Knex is initialized.
var knex = require('knex')({
client: 'mysql',
connection: {}
});
I setup knex to be mocked in my unit test.
myService = proxyquire('../app/myService', {
'knex': knexProxy
});
My service includes knex.
var knex = require('knex').knex,
When my service runs a query, it fails.
var sql = knex("table_name");
sql.insert(rowToInsert, "auto_increment_id");
sql.then(function (insertId) {
resolve();
}, function (err) {
reject(err);
});
For some reason I just can't seem to capture the request before it attempts the connection.
I've also, tried to create a custom Knex Client, but that hasn't worked yet either.
Using jest:
Create the file /__mocks__/knex.js in your app root:
module.exports = () => ({
select: jest.fn().mockReturnThis(),
from: jest.fn().mockReturnThis(),
where: jest.fn().mockReturnThis(),
first: jest.fn().mockReturnThis(),
then: jest.fn(function (done) {
done(null)
})
})
Pass the desired return value to done
I have been using in-memory Sqlite3 databases for automated testing with great success. It's not true unit testing however it does run much faster than MySQL or PostgreSQL. I have posted more details about this solution on a different question.
I used jest to mock knex but I had to define an object that contains the method that I used.
not the most elegant solution but is working
let knexMock = () => {
const fn = () => {
return {
returning: function() {
return {
insert: jest.fn().mockImplementation(() => [123123])
}
},
insert: jest.fn()
}
}
fn.raw = jest.fn()
return fn
}
knex.mockImplementation(knexMock)
I'm using jest and you can do something like this:
jest.mock('knex', () => {
const fn = () => {
return {
select: jest.fn().mockReturnThis(),
from: jest.fn().mockReturnThis(),
where: jest.fn().mockReturnThis(),
first: jest.fn().mockReturnThis(),
insert: jest.fn().mockReturnThis(),
raw: jest.fn().mockReturnThis(),
then: jest.fn(function (done) {
done(null)
})
}
}
return fn
})
I've written this tiny lib called knex-mock-client which does exactly this, it allows you to setup your db "connection" with a mockClient which will track your calls & help you with responses.
For example:
// my-cool-controller.ts
import { db } from '../common/db-setup';
export async function addUser(user: User): Promise<{ id }> {
const [insertId] = await db.insert(user).into('users');
return { id: insertId };
}
// my-cool-controller.spec.ts
import { expect } from '#jest/globals';
import knex, { Knex } from 'knex';
import { getTracker, MockClient } from 'knex-mock-client';
import faker from 'faker';
jest.mock('../common/db-setup', () => {
return knex({ client: MockClient });
});
describe('my-cool-controller tests', () => {
let tracker: Tracker;
beforeAll(() => {
tracker = getTracker();
});
afterEach(() => {
tracker.reset();
});
it('should add new user', async () => {
const insertId = faker.datatype.number();
tracker.on.insert('users').response([insertId]);
const newUser = { name: 'foo bar', email: 'test#test.com' };
const data = await addUser(newUser);
expect(data.id).toEqual(insertId);
const insertHistory = tracker.history.insert;
expect(insertHistory).toHaveLength(1);
expect(insertHistory[0].method).toEqual('insert');
expect(insertHistory[0].bindings).toEqual([newUser.name, newUser.email]);
});
});
This works for me, hope it helps someone:
//db.ts
import knex from 'knex';
const db = knex({
client: 'pg',
connection: {},
pool: { min: 0, max: 1 }
});
export default db('someTableName');
//myFunction.ts
//somewhere inside a function
const data = await db
// 👇 Knex query builders are mutable so when re-using them .clone() is necessary.
.clone()
.where({ pk: 'someId', sk: 'someId2' })
.select('data')
.orderBy('inserted_at', 'desc')
.first()
//myFunction.test.ts
describe("myFunction", () => {
beforeEach(() => {
jest.spyOn(db, "clone").mockImplementation(() => db);
jest.spyOn(db, "select");
jest.spyOn(db, "where");
jest.spyOn(db, "orderBy");
});
afterEach(() => {
jest.clearAllMocks();
});
it("should work as expected", async () => {
jest.spyOn(db, "first").mockResolvedValueOnce("desiredReturnValue");
await myFunction();
expect(db.where).toHaveBeenCalledWith({
pk: "someId",
sk: "someId2",
});
expect(db.select).toHaveBeenCalledWith("data");
expect(db.orderBy).toHaveBeenCalledWith("inserted_at", "desc");
expect(db.first).toHaveBeenCalledTimes(1);
});
});

Categories

Resources