I'm not able to test my sails.js controller with chai and sinon - unit-testing

I have a controller Acounts with a method sum, and a service file named validation with an object register. This object has a method validate that validates the form provided and returns a Boolean.
Controller
sum: function(req, res) {
//validate form with a validation service function
const validator = new validation.register();
let check = validator.validate(req.body.form);
let count = 0;
if (check) {
count += 1;
} else {
count -= 1;
}
res.send(count);
},
test
//imports
const chai = require("chai");
const expect = chai.expect;
const sinon = require("sinon");
const util = require('util'); // to print complex objects
const acountsC = require("../../../api/controllers/AcountsController.js");
describe("AcountsController", function() {
describe("sum", function() {
let req = {
body: {
form: {}
}
}
let res = {
send: sinon.spy()
}
let validation = {
register: {
validate: function() {}
}
}
let stub_validate = sinon.stub(validation.register, "validate").returns(true);
it("count should be 1 when validation is true", function() {
acountsC.sum(req, res);
expect(count).to.equal(1);
});
});
});
test log
AcountsController
sum
1) count should be 1 when validation is true
0 passing (5s)
1 failing
1) AcountsController
sum
count should be 1 when validation is true:
ReferenceError: count is not defined
note
I understand that the test is supposed to execute the piece of code we are calling, while replacing the external functions called in that piece of code(The controller) making it return whatever we set. If the test is executing that piece of code, why I can't access any variables created in the controller?
I've tried by spying res.send(), and check if it was called with a 1. I didn't succeed.
I searched everywhere how to perform an assertion to a variable, but I found nothing. :(
hope you can help

Here is the unit test solution:
accountController.js:
const validation = require('./validation');
class AccountController {
sum(req, res) {
const validator = new validation.register();
const check = validator.validate(req.body.form);
let count = 0;
if (check) {
count += 1;
} else {
count -= 1;
}
res.send(count);
}
}
module.exports = AccountController;
validation.js:
class Register {
validate() {}
}
module.exports = {
register: Register,
};
accountController.test.js:
const AccountController = require('./accountController');
const sinon = require('sinon');
const validation = require('./validation');
describe('60182912', () => {
afterEach(() => {
sinon.restore();
});
describe('#sum', () => {
it('should increase count and send', () => {
const registerInstanceStub = {
validate: sinon.stub().returns(true),
};
const registerStub = sinon.stub(validation, 'register').callsFake(() => registerInstanceStub);
const accountController = new AccountController();
const mRes = { send: sinon.stub() };
const mReq = { body: { form: {} } };
accountController.sum(mReq, mRes);
sinon.assert.calledWithExactly(mRes.send, 1);
sinon.assert.calledOnce(registerStub);
sinon.assert.calledWithExactly(registerInstanceStub.validate, {});
});
it('should decrease count and send', () => {
const registerInstanceStub = {
validate: sinon.stub().returns(false),
};
const registerStub = sinon.stub(validation, 'register').callsFake(() => registerInstanceStub);
const accountController = new AccountController();
const mRes = { send: sinon.stub() };
const mReq = { body: { form: {} } };
accountController.sum(mReq, mRes);
sinon.assert.calledWithExactly(mRes.send, -1);
sinon.assert.calledOnce(registerStub);
sinon.assert.calledWithExactly(registerInstanceStub.validate, {});
});
});
});
Unit test results with coverage report:
60182912
#sum
✓ should increase count and send
✓ should decrease count and send
2 passing (10ms)
----------------------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
----------------------|---------|----------|---------|---------|-------------------
All files | 100 | 100 | 50 | 100 |
accountController.js | 100 | 100 | 100 | 100 |
validation.js | 100 | 100 | 0 | 100 |
----------------------|---------|----------|---------|---------|-------------------
source code: https://github.com/mrdulin/expressjs-research/tree/master/src/stackoverflow/60182912

The problem was the lifecycle file I created trusting in sails docs. That documentation is for integrated testing due to the fact that it lifts sails before any other test. This is quite slow while unit tests should be fast. Erasing that file was enough to test the controller successfully. Otherwise sails messes up with the tests in a way I don't even fully understand. I suppose it is due to sails making services globally available. So when my controller calls for validation service, this one returns some default and not what the stub says it should return.
UPDATE:
I managed to make it work. When lifting sails before testing, only the tested controller should be require, services and models shouldn't.

Related

Test case fail for method returning from soap.createClientAsync function

I have a soap RestAPI to call for excecuting a function from a service for which i have used soap lib with async await. The code is working fine. When comes to unit testing the test case fails at the callback method returning from the client. The code and UT error follows.
Function to call Soap Client - Code - Helper.ts
class soapHelper {
public async registerInSoap(
uploadRequest: ImportExperimentRequestDto,
): Promise<{ RegisterExperimentResult: IffManPublishResponseDto }> {
const url = "http://test.com/Services/Req.svc?wsdl";
const client = await soap.createClientAsync(flavourUrl);
return client.RegisterExperimentAsync({ upload: uploadRequest});
}
}
Test Case - Code
describe("**** Register via soap service ****", () => {
it("** should excecute register method **", async () => {
const request = cloneDeep(MOCK.API_PAYLOAD);
const clientResponse = {
RegisterExperimentAsync: jest.fn(),
};
jest.spyOn<any, any>(soap, "createClientAsync").mockReturnValueOnce(() => Promise.resolve(clientResponse));
const result= await soapHelper.registerInSoap(request);
expect(result).toEqual({ Result: AFB_MOCK_RESPONSE.API_RESPONSE });
});
});
Error
TypeError: client.RegisterExperimentAsync is not a function
enter image description here
The mock resolved value of soap.createClientAsync() should be a soap client.
E.g.
index.ts:
import * as soap from 'soap';
interface ImportExperimentRequestDto {}
interface IffManPublishResponseDto {}
export class SoapHelper {
public async registerInSoap(
uploadRequest: ImportExperimentRequestDto,
): Promise<{ RegisterExperimentResult: IffManPublishResponseDto }> {
const url = 'http://test.com/Services/Req.svc?wsdl';
const client = await soap.createClientAsync(url);
return client.RegisterExperimentAsync({ upload: uploadRequest });
}
}
index.test.ts:
import { Client } from 'soap';
import * as soap from 'soap';
import { SoapHelper } from '.';
describe('**** Register via soap service ****', () => {
it('** should excecute register method **', async () => {
const request = {};
const mClient = ({
RegisterExperimentAsync: jest.fn().mockResolvedValueOnce({ Result: 'mock result' }),
} as unknown) as Client;
jest.spyOn(soap, 'createClientAsync').mockResolvedValue(mClient);
const soapHelper = new SoapHelper();
const result = await soapHelper.registerInSoap(request);
expect(result).toEqual({Result: 'mock result'});
});
});
Test result:
PASS stackoverflow/72604110/index.test.ts (9.87 s)
**** Register via soap service ****
✓ ** should excecute register method ** (3 ms)
----------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
----------|---------|----------|---------|---------|-------------------
All files | 100 | 100 | 100 | 100 |
index.ts | 100 | 100 | 100 | 100 |
----------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 10.471 s

I am mocking two functions exactly the same way. In one case the mock value is returned and in another case the real function is called. Why?

I have a file that exports some functions:
function getNow() {
console.log('real now');
return dayjs();
}
function groupProducts(productInfos, now) {
console.log('real group');
return productInfos.reduce((groups, productInfo) => {
const groupKey = dayjs(productInfo.saleStartDate) > now ? dayjs(productInfo.saleStartDate).format('YYYY-MM-DD') : dayjs(now).format('YYYY-MM-DD');
let group = groups[groupKey];
if (!group) {
group = [];
// eslint-disable-next-line no-param-reassign
groups[groupKey] = group;
}
group.push(productInfo.itemId);
return groups;
}, {});
}
async function fetchProducts(client, productInfos, now) {
const products = [];
const groups = groupProducts(productInfos, now);
for (const [date, ids] of Object.entries(productQueryGroups)) {
// eslint-disable-next-line no-await-in-loop
const productBatch = await fetchResources(
client.queryProducts,
{
articleIds: ids,
timestamp: date,
},
);
products.push(...productBatch);
}
return products;
}
module.exports = {
test: {
getNow,
groupProducts,
fetchProducts,
},
};
I run my tests with:
package.json script
"testw": "npx ../node_modules/.bin/jest --watch",
cli command:
npm run testw -- filename
In this test I exercise groupProducts and mock getNow. The real getNow is never called and the test passes.
describe('groupProducts', () => {
it('groups productInfo ids into today or future date groups', () => {
// Arrange
const nowSpy = jest.spyOn(test, 'getNow').mockReturnValue(dayjs('2001-02-03T04:05:06.007Z'));
const expectedMap = {
'2001-02-03': ['Art-Past', 'Art-Today'],
'2002-12-31': ['Art-Future-1', 'Art-Future-2'],
'2003-12-31': ['Art-Other-Future'],
};
const productInfos = [{
itemId: 'Art-Past',
saleStartDate: '1999-01-01',
}, {
itemId: 'Art-Today',
saleStartDate: '2001-02-03',
}, {
itemId: 'Art-Future-1',
saleStartDate: '2002-12-31',
}, {
itemId: 'Art-Future-2',
saleStartDate: '2002-12-31',
}, {
itemId: 'Art-Other-Future',
saleStartDate: '2003-12-31',
}];
// Assert
const dateToIds = test.groupProductInfosByTodayOrFutureDate(productInfos, test.getNow());
// Expect
expect(dateToIds).toEqual(expectedMap);
// Restore
nowSpy.mockRestore();
});
});
In this test I exercise fetchProducts and mock groupProducts. The real groupProducts is called and the causes the test to fail.
describe('fetchProducts', () => {
it.only('calls fetchResources with the timestamp and date for every product query group', async () => {
// Arrange
const productQueryGroups = {
[test.PRICE_GROUPS.CURRENT]: ['Art-Past', 'Art-Today'],
[test.PRICE_GROUPS.FUTURE]: ['Art-Future-1', 'Art-Future-2', 'Art-Other-Future'],
};
const groupProductsSpy = jest.spyOn(test, 'groupProducts').mockReturnValue( productQueryGroups);
const fetchResourcesSpy = jest.spyOn(test, 'fetchResources').mockResolvedValue([]);
// Act
await test.fetchProducts();
// Expect
expect(test.fetchResources).toHaveBeenCalledWith(expect.anything(), expect.objectContaining({ articleIds: [productQueryGroups[test.PRICE_GROUPS.CURRENT]], timestamp: test.PRICE_GROUPS.CURRENT }));
// Restore
groupProductsSpy.mockRestore();
fetchResourcesSpy.mockRestore();
});
});
Error message
98 | function groupProducts(productInfos, now) {
> 99 | return productInfos.reduce((groups, productInfo) => {
| ^
100 | const groupKey = dayjs(productInfo.saleStartDate) > now ? dayjs(productInfo.saleStartDate).format('YYYY-MM-DD') : dayjs(now).format('YYYY-MM-DD');
101 |
102 | let group = groups[groupKey];
Why is the real groupProducts called? To me it looks completely analogous to the previous example.

Unit testing for Google Cloud Functions

I was trying to write unit testing for Google Cloud Functions referring to Google Cloud Platform unit testing docs but not able to understand how to mock when Google Cloud Storage is used. Below is sample code for which I am trying to write unit testing :
exports.listFiles = async(req, res) => {
let bucketName = req.body.bucket || req.query.bucket
// Import the Google Cloud Storage library
const {Storage} = require('#google-cloud/storage');
// Initiate a Storage client
const storage = new Storage();
// List files in the bucket and store their name in the array called 'result'
const [files] = await storage.bucket(bucketName).getFiles();
let result = [];
files.forEach(file => {
result.push(file.name);
});
// Send the result upon a successful request
res.status(200).send(result);
};
Here is the unit test solution:
index.js:
exports.listFiles = async (req, res) => {
const bucketName = req.body.bucket || req.query.bucket;
const { Storage } = require("#google-cloud/storage");
const storage = new Storage();
const [files] = await storage.bucket(bucketName).getFiles();
const result = [];
files.forEach((file) => {
result.push(file.name);
});
res.status(200).send(result);
};
index.spec.js:
const sinon = require("sinon");
const { Storage } = require("#google-cloud/storage");
const { listFiles } = require(".");
describe("listFiles", () => {
afterEach(() => {
sinon.restore();
});
it("should pass", async () => {
const mFiles = [{ name: "sinon" }, { name: "mocha" }, { name: "chai" }];
const getFilesStub = sinon.stub().resolves([mFiles]);
const bucketStub = sinon.stub(Storage.prototype, "bucket").callsFake(() => ({ getFiles: getFilesStub }));
const mReq = { body: { bucket: "xxx-dev" }, query: { bucket: "xxx-release" } };
const mRes = { status: sinon.stub().returnsThis(), send: sinon.stub() };
await listFiles(mReq, mRes);
sinon.assert.calledWith(bucketStub, "xxx-dev");
sinon.assert.calledOnce(getFilesStub);
sinon.assert.calledWith(mRes.status, 200);
sinon.assert.calledWith(mRes.send, ["sinon", "mocha", "chai"]);
});
});
Unit test result with coverage report:
listFiles
✓ should pass
1 passing (12ms)
---------------|----------|----------|----------|----------|-------------------|
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |
---------------|----------|----------|----------|----------|-------------------|
All files | 100 | 50 | 100 | 100 | |
index.js | 100 | 50 | 100 | 100 | 2 |
index.spec.js | 100 | 100 | 100 | 100 | |
---------------|----------|----------|----------|----------|-------------------|
Source code: https://github.com/mrdulin/mocha-chai-sinon-codelab/tree/master/src/stackoverflow/59283121
There is no emulator for offline usage for GCS service link.
Some developers created their own mock classes to emulate Google Cloud Storage link.
Also, it was suggested for offline development to use HTTP request incerceptors library link:
const gcs = new Storage()
gcs.interceptors.push({
request: reqOpts => {
console.log(reqOpts)
return reqOpts
}
})
This will log all HTTP requests we make in the format the request module accepts.
Also, if you pass customEndpoint: true, it will skip the authentication step:
const Storage = require('#google-cloud/storage')
const gcs = new Storage({ customEndpoint: true })
In python is possible to implement more of a unit testing context link:
```def test_read_sql(self):
storage_client = mock.create_autospec(storage.Client)
mock_bucket = mock.create_autospec(storage.Bucket)
mock_blob = mock.create_autospec(storage.Blob)
mock_bucket.return_value = mock_blob
storage_client.get_bucket.return_value = mock_bucket
mock_bucket.get_blob.return_value = mock_blob
Here you can find more documentation:
Testing Storage Triggered Functions
For simplifying my testing steps I extract the API calls in my own class and I write an interface that I can mock during my tests.
My target is above all to test my business logic, and not the API call (I assume that it works as expected).

Testing a React Store with Jest

I'm trying to test my React store using Jest (I'm adhering to the Flux architecture) but am not sure when I need to mock up a store function. To illustrate my point, here's a portion of my store code:
export default class AdminStore extends EventEmitter {
constructor() {
super();
this.users = [];
this.userTotal = 0;
this.profile = {};
AdminDispatcher.register((payload) => {
switch (payload.eventName) {
case AdminConstants.USER_TOTAL:
this.userTotal = payload.total;
this.emit('change');
break;
case AdminConstants.ALL_USERS:
this.users = payload.users;
this.emit('change');
break;
case AdminConstants.FETCH_USER:
this.profile = payload.profile;
this.emit('change');
break;
default:
break;
}
return true;
});
}
syncUsers() {
return this.users;
}
syncUserTotal() {
return this.userTotal;
}
syncProfile() {
return this.profile;
}
}
Here's a snippet of my Jest test code:
jest.dontMock('../AdminStore.js');
jest.dontMock('../../constants/AdminConstants.js');
describe('AdminStore', () => {
let callback;
let AdminStore;
let AdminDispatcher;
let AdminConstants = require('../../constants/AdminConstants.js');
beforeEach(() => {
AdminStore = require('../AdminStore.js');
AdminDispatcher = require('../../dispatcher/AdminDispatcher.js');
callback = AdminDispatcher.register.mock.calls[0];
});
it ('should initialize with a user total of 0', () => {
let total = AdminStore.syncUserTotal();
expect(total).toEqual(0);
});
});
My Jest output for this test looks like this:
● AdminStore › it should initialize with a user total of 0
- TypeError: undefined is not a function
at Spec.<anonymous> (/Users/seanchen/karma/admin-new/src/js/stores/__tests__/AdminStore-test.js:34:32)
at jasmine.Block.execute (/Users/seanchen/karma/admin-new/node_modules/jest-cli/vendor/jasmine/jasmine-1.3.0.js:1065:17)
at jasmine.Queue.next_ (/Users/seanchen/karma/admin-new/node_modules/jest-cli/vendor/jasmine/jasmine-1.3.0.js:2098:31)
at null._onTimeout (/Users/seanchen/karma/admin-new/node_modules/jest-cli/vendor/jasmine/jasmine-1.3.0.js:2088:18)
1 test failed, 1 test passed (2 total)
Run time: 3.493s
Does AdminStore.syncUserTotal() need to be mocked? I'm really not sure why it's undefined.
You don't have an instance of AdminStore.
Your AdminStore is exporting the class itself, not an instance of the class.
Try something like
it ('should initialize with a user total of 0', () => {
let adminStore = new AdminStore();
let total = adminStore.syncUserTotal();
expect(total).toEqual(0);
});

Angularjs how can I unit test a Service which depends on another Service with promises?

How can I test a service which depends on another service. I currently get Service1Provider not found error in this implementation. How can I properly inject Service1 so I can unit test Service2? Thanks for any tips or tricks.
jsfiddle
gist
!function(ng){
'use strict';
var module = ng.module('foo.services', []);
(function($ng, $module) {
function Service($q) {
return {
bar: function(a,b,c){
var baz = a+b+c;
return function(d,e,f){
var deferred = $q.defer();
if(baz > 0){
deferred.resolve({result: baz + d + e + f });
} else {
deferred.reject({ err: 'baz was <= 0'})
}
return deferred.promise;
}
}
};
}
$module.factory("Service1", ['$q', Service]);
})(ng, module);
(function($ng, $module) {
function Service(Service1) {
function doSomething(){
var result;
var whatever = Service1.bar(5,6,7);
var promise = whatever(8,9,10);
promise.then(function(data){
result = data.result;
//data.result should be 45 here
}, function(err){
});
return result;
}
return {
bam:doSomething
};
}
$module.factory("Service2", ["Service1", Service]);
})(ng, module);
}(angular);
var myApp = angular.module('myApp',['foo.services']);
If you are just testing Service2 then you should try to eliminate any dependency on Service1 in the test. Your test could have something like:
module('foo.services', function($provide) {
$provide.value('Service1', MockService1());
});
This will give the return value from the function MockService1 instead of actually using the service, whenever it is injected.
Then you have MockService1 function return a skeleton of the actual service with the same function. In your test you can then wait for the promise to be resolved by doing something like this:
bar: function(...) {
var def = $q.defer();
$timeout(function() {
def.resolve('test');
});
return def.promise;
}
// This is in your test
bar.then( /* .... some tests */ );
// This executes the timeout and therefor the resolve
$rootScope.$digest();
$timeout.flush();
Hope this helped