cannot get rid of date unit testing - javascript - mocha.js

I'm new to unit testing and I implementing a unit test for a function that returns date.
const getLastHour = () => {
const date = new Date().getTime() - 3600000;
return date;
};
I made something like this but it deosn't work
describe('getLastHour', () => {
const getLastHour = mobileArticleService.__get__('getLastHour');
const getTime = sinon.stub(2019052906);
const Date = sinon.stub({ getTime });
before(() => {
mobileArticleService.__set__({
Date,
getTime,
});
});
it('should return date with hours ', () => {
expect(Date).to.be.called;
expect(getTime).to.be.called;
});
});

You are not actually stubbing the Date in your current implementation and not calling your unit under test. If you are trying to unit test that function, stub the dependancies. Unit tests focus a lot on inputs and outputs of a single unit of work.
describe('My Test', function () {
const dateStub;
before('', function () {
dateStub = sinon.stub(Date, 'getTime').returns(143434325325);
});
it('should return date with hours ', () => {
const result = getLastHour();
expect(dateStub).to.be.called;
});
});

Related

Is there a way to spy on a Promise constructor?

I have this function
public pick(config?: FilePickerConfig): Promise<FilePickerResult> {
return new Promise<FilePickerResult>(resolve => {
this.pickWithCallbacks(resolve, resolve, config);
});
}
I want to test if the call to this.pickWithCallbacks has as first and second parameter the resolve parameter of the function.
Is there a way to do this in jest or jasmine? I have tried to spy on window, 'Promise' but it does not work.
Edit: It is not a depulicate of Spying on a constructor using Jasmine because that is what I have tried and did not work.
I have tried this:
const dummyResolve = () => { };
const promiseSpy = spyOn(window, 'Promise').and.callFake((dummyResolve)=>{});
const pickWithCallbacksSpy = spyOn(sut, 'pickWithCallbacks');
sut.pick();
expect(pickWithCallbacksSpy).toHaveBeenCalledWith(dummyResolve, dummyResolve, undefined);
So finally I just left the Promise do his thing and I captured the resolve callback
test('on success should call pickWithCallbacks with the resolve function of a promise', (done) => {
const cordovaExecSpy = spyOn(sut, 'pickWithCallbacks');
const dummyReturn = {};
sut.pick().then(obtained => {
expect(obtained).toBe(dummyReturn);
done();
});
const capturedOnSucess = cordovaExecSpy.calls.mostRecent().args[0];
capturedOnSucess(dummyReturn);
});
test('on Error should call pickWithCallbacks with the resolve function of a promise', (done) => {
const cordovaExecSpy = spyOn(sut, 'pickWithCallbacks');
const dummyReturn = {};
sut.pick().then(obtained => {
expect(obtained).toBe(dummyReturn);
done();
});
const capturedOnError = cordovaExecSpy.calls.mostRecent().args[1];
capturedOnError(dummyReturn);
});

How to use sinon to mock a series of knex call

Given a myknex.js:
export default { return require('knex')({client:'mysql',connection:{//conn}})}
and I want to write a unit test for the following function:
async function get({ userId }) {
return await myknex('users')
.where({ id: userId })
.returning('*');
}
The unit test looks like:
const sinon = require('sinon');
const sandbox = sinon.createSandbox();
const { myknex } = require('./myknex');
it('no record', async () => {
sandbox
.stub(myknex('users'), 'executeQuery').resolves([]);
const result = await myrepo.get({ userId: 1 });
const expectedResult = [];
expect(result).to.deep.equal(expectedResult);
});
I got an error message about:
TypeError: Cannot stub non-existent own property executeQuery
How can I mock the chained myknex calls?
Since you have myknex.js export a function of knex, we need to use proxyquire to mock this in test file.
const chai = require('chai');
const expect = chai.expect;
const sinon = require('sinon');
const proxyquire = require('proxyquire'); // include proxyquire
const expectedResult = [];
const knexQuery = {
where: sinon.stub().returnsThis(), // because we want to call `returning` afterwards
returning: sinon.stub().resolves(expectedResult) // resolve it as promise
}
const myknexStub = sinon.stub().returns(knexQuery);
const myrepo = proxyquire('./src', { './myknex': myknexStub }); // your source file and stub myknex here
describe(('Test My Module'), () => {
it('no record', async () => {
const result = await myrepo.get({ userId: 1 });
// many options to unit test the function
expect(myknexStub.calledWith('users')).to.be.ok;
expect(knexQuery.where.calledWith({ id: 1 })).to.be.ok;
expect(knexQuery.returning.calledWith('*')).to.be.ok;
expect(knexQuery.returning.calledAfter(knexQuery.where)).to.be.ok;
expect(result).to.deep.equal(expectedResult);
});
});
Hope it helps

Testing if method with Promise was called (Jest)

I have an initializer method calling another method that returns a promise, like:
initStuffAfterLoad() {
const _this = this;
const theInterval = window.setInterval(function() {
if (thing) {
window.clearInterval(theInterval);
_this.getBanana()
.then(response => {
_this.getApple(response, _this);
});
}
}, 100);
}
and am needing to test whether getBanana was called (jest/sinon). So far I have:
test('init function calls getBanana', () => {
let thing = true
const getBananaSpy = sinon.spy();
sinon.stub(TheClass.prototype, 'getBanana').callsFake(getBananaSpy).resolves();
jest.useFakeTimers();
TheClass.prototype.initStuffAfterLoad();
jest.runOnlylPendingTimers();
expect(getBananaSpy.called).toBeTruthy();
TheClass.prototype.getBanana.restore();
});
However it still receives false at the assertion. I figure I'm not handling the Promise part correctly - what is the best practice way to do this?
I am not familiar with sinon, but here is a way to achieve your need with pure jest (even better it also checks that getApple is called when getBanana reseolves :))
jest.useFakeTimers()
const _this = {
getBanana: () => {},
getApple: () => {}
}
const initStuffAfterLoad = () => {
const theInterval = window.setInterval(function() {
window.clearInterval(theInterval);
_this.getBanana().then(response => {
_this.getApple(response, _this)
});
}, 100);
}
test('', () => {
let result
_this.getBanana = jest.fn(() => {
result = new Promise( resolve => { resolve() } )
return result
})
_this.getApple = jest.fn()
initStuffAfterLoad()
jest.runAllTimers()
expect(_this.getBanana.mock.calls.length).toBe(1)
return result.then(() => {
expect(_this.getApple.mock.calls.length).toBe(1)
})
})
code tested :)
PASS test\temp.test.js √ (25ms)
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 2.489s

chai-as-promised should.eventually.equal not passing

I am trying to write a minimum working example of chai-as-promised in order to understand how it is working when testing functions that return a promise.
I have the following function:
simple.test = async (input) => {
return input;
};
and the following test function:
chai.use(sinonChai);
chai.use(chaiAsPromised);
const { expect } = chai;
const should = chai.should();
describe('#Test', () => {
it('test', () => {
expect(simple.test(1)).should.eventually.equal(1);
});
});
However, testing this results in the test not passing, but in a very long error, which is pasted here: https://pastebin.com/fppecStx
Question: Is there something wrong about the code, or what seems to be the problem here?
First: Your mixing expect and should. If you want to use should for assertion, you don't need expect.
Second: To tell mocha that a test is async you have to either call done, return a Promise or use async/await.
const chai = require('chai');
const chaiAsPromised = require('chai-as-promised');
const sinonChai = require('sinon-chai');
const should = chai.should();
chai.use(sinonChai);
chai.use(chaiAsPromised);
// Function to test
const simple = {
test: async (input) => {
return input;
}
}
// Test
describe('#Test', () => {
it('test', () => {
return simple.test(1).should.eventually.equal(1);
});
});

How to test observable containing a debounce operator?

How does one write a Jasmine test to test an observable with the debounce operator? I've followed this blog post and understand the principles of how it should be tested, but it just doesn't seem to work.
Below is the factory that I am using to create the observable:
import Rx from "rx/dist/rx.all";
import DOMFactory from "../utils/dom-factory";
import usernameService from "./username.service";
function createUsernameComponent(config) {
const element = DOMFactory(config);
const username = Rx.Observable
.fromEvent(element.find('input'), 'input')
.pluck('target', 'value')
.startWith(config.value);
const isAvailable = username
.debounce(500)
.tap(() => console.info('I am never called!'))
.flatMapLatest(usernameService.isAvailable)
.startWith(false);
const usernameStream = Rx.Observable.combineLatest(username, isAvailable)
.map((results) => {
const [username, isAvailable] = results;
return isAvailable ? username : ''
})
.distinctUntilChanged();
return Object.freeze({
stream: usernameStream,
view: element
});
}
export default createUsernameComponent;
Note that tap operator is never called by the test. However, it will be executed properly if I run this code on the browser.
Below is my attempt at the test:
import Rx from "rx/dist/rx.all";
import Username from "./username.component";
import DataItemBuilder from "../../../test/js/utils/c+j-builders";
import usernameService from "./username.service"
describe('Username Component', () => {
let input, username;
beforeEach(() => {
const usernameConfig = DataItemBuilder.withName('foo')
.withPrompt('label').withType('text').build();
const usernameComponent = Username(usernameConfig);
usernameComponent.stream.subscribe(value => username = value);
input = usernameComponent.view.find('input');
});
it('should set to a valid username after debounce', () => {
const scheduler = injectTestSchedulerIntoDebounce();
scheduler.scheduleRelative(null, 1000, () => {
doKeyUpTest('abcddd', 'abcdd');
scheduler.stop();
});
scheduler.start();
scheduler.advanceTo(1000);
});
function injectTestSchedulerIntoDebounce() {
const originalOperator = Rx.Observable.prototype.debounce;
const scheduler = new Rx.TestScheduler();
spyOn(Rx.Observable.prototype, 'debounce').and.callFake((dueTime) => {
console.info('The mocked debounce is never called!');
if (typeof dueTime === 'number') {
return originalOperator.call(this, dueTime, scheduler);
}
return originalOperator.call(this, dueTime);
});
return scheduler;
}
function doKeyUpTest(inputValue, expectation) {
input.val(inputValue);
input.trigger('input');
expect(username).toBe(expectation);
}
});
When I run the test, the fake debounce never gets called. I plan to mock the username service once I can get past the debounce.
In your test code you are triggering the input event inside the scheduleRelative function. This doesn't work because you are advancing 1000ms before doing the change. The debouncer then waits 500ms to debounce the isAvailable call but you already stopped the scheduler so time is not advancing afterwards.
What you should do is: trigger the input event before advancing the scheduler time or even better in a scheduleRelative function for a time <= 500ms in a and then inside the scheduleRelative function for 1000ms you have to call the expect function with the expected output and then stop the scheduler.
It should look like this:
it('should set to a valid username after debounce', () => {
const scheduler = injectTestSchedulerIntoDebounce();
scheduler.scheduleRelative(null, 500, () => {
input.val(inputValue);
input.trigger('input');
});
scheduler.scheduleRelative(null, 1000, () => {
expect(username).toBe(expectation);
scheduler.stop();
});
scheduler.start();
scheduler.advanceTo(1000);
});
In addition to that I have better experience with scheduleAbsolute instead of scheduleRelative because it is less confusing.
As per Simon Jentsch's answer, below is the answer using scheduleAbsolute instead of scheduleRelative:
import Rx from "rx/dist/rx.all";
import Username from "./username.component";
import DataItemBuilder from "../../../test/js/utils/c+j-builders";
import usernameService from "./username.service"
describe('Username Component', () => {
let input, username, promiseHelper;
const scheduler = new Rx.TestScheduler(0);
beforeEach(() => {
spyOn(usernameService, 'isAvailable').and.callFake(() => {
return Rx.Observable.just(true);
});
});
beforeEach(() => {
const usernameConfig = DataItemBuilder.withName('foo')
.withPrompt('label').withType('text').build();
const usernameComponent = Username(usernameConfig, scheduler);
usernameComponent.stream.subscribe(value => username = value);
input = usernameComponent.view.find('input');
});
it('should set the username for valid input after debounce', (done) => {
doKeyUpTest('abcddd', '');
scheduler.scheduleAbsolute(null, 100, () => {
expect(usernameService.isAvailable).not.toHaveBeenCalled();
expect(username).toBe('');
});
scheduler.scheduleAbsolute(null, 1000, () => {
expect(usernameService.isAvailable).toHaveBeenCalled();
expect(username).toBe('abcddd');
scheduler.stop();
done();
});
scheduler.start();
});
function doKeyUpTest(inputValue, expectation) {
input.val(inputValue);
input.trigger('input');
expect(username).toBe(expectation);
}
});

Resources