Angular, Jasmine - Spying on a function, being executed inside $interval/$timeout services - javascript

Good day!
I want to test whether parseUrl method is called, but as the result: test is failed - parseUrl was never called, although if I put the parseUrl() outside of the $interval service's function, then test pass successfully.
Here is the place, where parseUrl is called:
testMethod() {
let intervalPromise = this._$interval(() => {
this.parseUrl('test', 'test_code_name');
}, 500);
}
parseUrl(url, name) {}
Here is the test case:
it('Expect testMethod to be called', function () {
TestService.testMethod()
spyOn(TestService, 'parseUrl').and.callThrough();
expect(TestService.parseUrl).toHaveBeenCalled();
});
Also the fact is, if I put the testMethod() outside of the $interval service, then test pass successfully.

In your test you need to inject the $interval service (could use ngMock) and call $interval.flush(500);
var $interval;
angular.mock.inject(function (myModuleService, _$interval_) {
// Initialize the service under test instance
$interval = _$interval_;
});
it('Expect testMethod to be called', function () {
TestService.testMethod()
spyOn(TestService, 'parseUrl').and.callThrough();
$interval.flush(500);
expect(TestService.parseUrl).toHaveBeenCalled();
});
Here is a link to the docs: https://docs.angularjs.org/api/ngMock/service/$interval
Use $interval.flush(millis) to move forward by millis milliseconds and trigger any functions scheduled to run in that time.

Related

Why a test with function called from another function in the mocked class fail?

I have an issue with testing a pretty simple of javascript code using jest. So I post the code:
Foo.js
export class Foo {
constructor() {
}
someFn() {
this.internalFn();
}
internalFn() {
}
}
Foo.spec.js
jest.mock('../src/foo');
import {Foo} from '../src/foo';
describe('Foo', () => {
it ('test foo', () => {
const foo = new Foo();
foo.someFn();
expect(foo.someFn.mock.calls.length).toBe(1);
expect(foo.internalFn.mock.calls.length).toBe(1); // why received 0 ???
})
})
Why second expect fail? foo.internalFn is called from foo.someFn.
A mock function will actually not do anything (except for counting how often it was called, etc.)
That's why your internalFn() will bever be called, when you mock the function someFn().
// EDIT: Just some clarifications
When you unit test your software you want to test it modular and isolated.
Functions should be able to work on their own even when they're called internally by other functions.
When you want to test the function someFn() you have to mock internalFn(), because you want to check if internalFn() was actually called by calling someFn().
If you want to to test anything else that will call someFn() you have to mock that instead.

Jest: how to count call from mock methods called via `call` or `apply`?

How can I use mocks to count function calls made via call or apply
// mylib.js
module.exports = {
requestInfo: function(model, id) {
return `The information for ${model} with ID ${id} is foobar`;
},
execute: function(name) {
return this[name] && this[name].apply(this, [].slice.call(arguments, 1));
},
};
// mylib.test.js
jest.mock('./mylib.js');
var myLib = require('./mylib.js');
test('', () => {
myLib.execute('requestInfo', 'Ferrari', '14523');
expect(myLib.execute.mock.calls.length).toBe(1); // Success!
expect(myLib.requestInfo.mock.calls.length).toBe(1); // FAIL
});
If I explicitly call myLib.requestInfo, the second expectation succeeds.
Is there a way to watch module mock calls whose functions were called via apply or call?
From the jest.mock doc:
Mocks a module with an auto-mocked version when it is being required.
The docs could probably be improved with a better description of what "auto-mocked version" means, but what happens is that Jest keeps the API surface of the module the same while replacing the implementation with empty mock functions.
So in this case execute is getting called but it has been replaced by an empty mock function so requestInfo never gets called which causes the test to fail.
To keep the implementation of execute intact you will want to avoid auto-mocking the entire module and instead spy on the original function with something like jest.spyOn:
var myLib = require('./mylib.js');
test('', () => {
jest.spyOn(myLib, 'execute'); // spy on execute
jest.spyOn(myLib, 'requestInfo') // spy on requestInfo...
.mockImplementation(() => {}); // ...and optionally replace the implementation
myLib.execute('requestInfo', 'Ferrari', '14523');
expect(myLib.execute.mock.calls.length).toBe(1); // SUCCESS
expect(myLib.requestInfo.mock.calls.length).toBe(1); // SUCCESS
});

Writing JavaScript tests that test other functions are called, without actually calling them

I have been tasked with writing unit tests for some AngularJS code that was written by another team, who didn't write any tests
They have written the following function but I cannot figure out how to test it
function showCallAlerts(callRecord, isInEditMode, callBack) {
var callAlerts = populateCallAlertOnEditCall(callRecord.callAlert);
var callModalInstance = openAlertModalInstance('Call', callAlerts, callBack);
if (callModalInstance !== undefined && callModalInstance !== null) {
callModalInstance.result.then(function() {
// Show equipment alerts based on company details
showEquipmentAlertsBasedOnCompanyDetails(callRecord, isInEditMode, callBack);
});
} else {
// Show equipment alerts based on company details
showEquipmentAlertsBasedOnCompanyDetails(callRecord, isInEditMode, callBack);
}
}
I need to test that each of the functions are called, not worrying about what they do as I'll test them separate, just that they are called.
When populateCallAlertOnEditCall is called it needs to either return an empty array or an array with some items in it
When openAlertModalInstance is called it needs to either return undefined or something that passes through to showEquipmentAlertsBasedOnCompanyDetails
showEquipmentAlertsBasedOnCompanyDetails should actually be called, I'll test that method separate, just that it was called
I have manged to write code to test simple functions but nothing like this one so any help will be much appreciated, I spent most of this afternoon trying to figure it out
You can use jasmine to mock the function calls that you are not interested in testing. For example, you can tell jasmine to return an empty array every time 'populateCallAlertOnEditCall' is called. I will write an example that might give you an insight:
describe('My Test Spec', function() {
var myController;
...
beforeEach( inject(($controller) => {
myController = $controller("myControllerName");
}));
it('Testing showCallAlerts when populateCallAlertOnEditCall returns an empty array', inject(function($controller) {
//setup
//this will replace every call to populateCallAlertOnEditCall with
//the function inside callFake
spyOn(myController, 'populateCallAlertOnEditCall ').and.callFake(function() {
return []; //returning an empty array.
});
//action
myController.showCallAlerts(...);
//assert
//Do your checking here.
}));
it('Testing showCallAlerts when populateCallAlertOnEditCall returns a non-empty array', inject(function($controller) {
//setup
//this will replace every call to populateCallAlertOnEditCall with
//the function inside callFake
spyOn(myController, 'populateCallAlertOnEditCall ').and.callFake(function() {
return [1,2,3,4]; //returning a non-empty array.
});
//action
myController.showCallAlerts(...);
//assert
//Do your checking here.
}));
});
the test that something has been called, you can use a Spy
your assertion would look like:
spyOn(obj, 'populateCallAlertOnEditCall')
expect(obj.method).toHaveBeenCalled()
UPDATED:
populateCallAlertOnEditCall = {}
spyOn(obj, 'populateCallAlertOnEditCall.result')
expect(obj.method).toHaveBeenCalled()
The kind of behaviour you want is called mocking
In Jasmine, mocking is done with Spy Objects, you can read more about those here
Basically, you can use mocks to test if functions were called with the expected parameters.
var xhr = mock( XMLHttpRequest );
xhr.send();
expect( xhr.send ).toHaveBeenCalled();

sandbox.useFakeTimers use cases

sinon.useFakeTimers() can stub global Date constructor new Date()
Which purposes and use cases has sandbox.useFakeTimers ?
From documentation
Fakes timers and binds the clock object to the sandbox such that it too is restored when calling sandbox.restore(). Access through sandbox.clock
it still unclear how to use the second method.
new Date() in SUT still returns original time-stamp
The idea is not to replace Date; it is to avoid waiting on setTimout as it says in the docs:
Fake timers is a synchronous implementation of setTimeout and friends
that Sinon.JS can overwrite the global functions with to allow you to
more easily test code using them
Here's an example on how to use it:
var assert = require('assert');
var sinon = require('sinon');
var executed = false;
function doSomething() {
setInterval(function() {
executed = true;
}, 10000);
}
describe('doSomething', function() {
beforeEach(function() {
this.clock = sinon.useFakeTimers();
});
afterEach(function() {
this.clock = sinon.restore();
});
it('should execute without waiting on the timeout', function(){
doSomething();
this.clock.tick(10001);
assert.equal(executed, true);
});
});
In this example, the function doSomething will execute after 10000 milliseconds. Instead of waiting on that to assert the test, one could simulate time passing by using this.clock.tick(10001) and then assert that the test is passing.

Sinon stub is not tracking consecutive methods invocation of the app

I'm trying to make a basic integration test using Karma, Sinon and Mocha, but it's not return the expected result.
The application has a simple flow:
todo.init --> calls todo.api.sendRequest({options Object}, callback) --> calls todo.setup
**todo.setup is being invoked by the callback when we get the response back.(from the sendRequest method)
var expect = chai.expect;
describe('API integration', function(){
it('todo.setup receives an array of todos when todo.init is called', function () {
// create a stub
sinon.stub(todo, 'setup');
// start the application
todo.init();
expect(todo.setup.called).to.be.true;
});
});
This test is not passing. If I invoke the todo.setup explicitly
var expect = chai.expect;
describe('API integration', function(){
it('todo.setup receives an array of todos when todo.init is called', function () {
// create a stub
sinon.stub(todo, 'setup');
// Invoke directly the todo.setup
todo.setup();
expect(todo.setup.called).to.be.true;
});
});
The test passes now, but it's not the correct behavior because i'm invoking manually the method instead the application call.

Categories