I have been trying to unit test my angular custom service written in Typescript. The service reads a global variable defined on the Window object. I have made it promise, so that in future I can make a AJAX call to get this information. Here is my stripped down service: -
export class ProxyDetectiveService {
public static $inject = [
$window,
$q
];
constructor(private $window:ng.IWindowService,
private $q:ng.IQService) {
}
public getProxyUserObject = ():ng.IPromise<any> => {
this.log.debug('Proxy User Service called, to get proxy user details');
var deferred = this.$q.defer();
var proxyDetails = this.$window.portalObject;
deferred.resolve(proxyDetails);
return deferred.promise;
};
}
My unit Test Case: -
describe('Proxy Detective Service - Unit Test Cases', () => {
var proxyDetectiveService:any,
$window:ng.IWindowService;
beforeEach(() => {
module('myApp');
});
beforeEach(inject(($injector:ng.auto.IInjectorService, _$window_) => {
proxyDetectiveService = $injector.get('ProxyDetectiveService');
_$window_ = {
portalObject: {
proxyUserDetails: {
firstName: 'testFN',
lastName: 'testLN'
}
}
};
}));
it('should have proxy object defined', function () {
var promise = proxyDetectiveService.getProxyUserObject();
promise.then(function (response) {
expect(response).toBeDefined();
}).catch(function (response) {
expect(response).toBeUndefined();
});
});
});
Here are my questions: -
My Test case gets executed, but I dont see the mocked window object in the service?
My promise then or catch clause never gets executed?
Are there any better ways I can implement my service? My intention is to return a promise, in future I may use AJAX call.
You need to use $provide to provide a mocked value in a unit test:
beforeEach(() => {
module('myApp', ($provide) => {
$provide.value('$window', myMockedWindowObject)
});
});
You also need to call $rootScope.$apply() to move promises forward in unit tests.
Related
I'm trying to write an angular unit test for a function that has a dependency on an imported function - how can write a unit test that mocks the results of the imported function? The function I have is:
import { DependentMethod } from './dependentMethod';
export function doSomething() {
const dependentResults = DependentMethod(); // DependentMethod makes an http call and returns the result
return dependentResults;
}
In this case, I want to test the doSomething and mock the DependentMethod function. When I've tried to mock stuff before, I've used spy on class methods but I'm not sure how to handle it in this case. Any help would be greatly appreciated!
You can try something as below,
import { dependent } from './dependentLibrary';
describe('yourCoponent', () => {
let component: yourComponent;
let service : dependentService;
beforeEach(() => {
service = new dependent(null); // if your service has further dependency
component = new yourComponent(service);
});
it('should perform test', () => {
// Arrange part -->
spyOn(service, 'dependentMethod').and.callFake(() => {
return Observable.from([ [1,2,3] ]); // return your mock data
});
//Act part
component.ngOnInit();
//Assert
expect(component.testable.action).toBe(value);
});
I would like to ask if can be tested calling the right function dependent on the condition with sinon or mocha. For example I have class Knight and I want to know if a function (knightRun) is called, when parameter 'data' is true.
export class Knight {
createKnight(data,reducer) {
if (data) {
this.knightRun(reducer);
} else if (!data) {
this.knightFight(reducer);
}
}
private knightFight(reducer) {
// do something
}
private knightRun(reducer) {
// do something
}
}
You can use spies to check whether a particular function has been called. Sinon.js is a library which provides a way to spy on functions when writing unit tests for your JavaScript.
e.g.
describe('Knight class', () => {
it('should call knightRun when data is false', () => {
const knight = new Knight().createKnight(false, null)
sinon.spy(knight, "knightRun")
assert(knight.knightRun.calledOnce)
})
it('should call knightFight when data is true', () => {
const knight = new Knight().createKnight(true, null)
sinon.spy(knight, "knightFight")
assert(knight.knightFight.calledOnce)
})
})
As an aside, the private keyword is not valid JavaScript.
I have the following angularjs based component which works fine, but looking at test coverage anything after window.addEventListener('message', is not covered.
Should I mock the window object and provide my own implementation for addEventListener? or spy on it and check its been called?
my.component.controller.ts
export class MyComponentController {
constructor() {}
public theEventOccurred(e: any) {
let json = JSON.parse(e.data);
console.log(json.document);
}
public $onInit() {
window.addEventListener('message', (event) => {
this.theEventOccurred(event);
}, false);
}
}
my.component.spec.ts
describe('Component: my', () => {
let $componentController: angular.IComponentControllerService;
let scope: angular.IScope;
beforeEach(inject(
($rootScope: angular.IScope,
_$componentController_: angular.IComponentControllerService) => {
scope = $rootScope.$new();
$componentController = _$componentController_;
}));
describe('Controller: MyComponentController', () => {
it('should log json.document', () => {
let ctrl: any = $componentController('my', { $scope: scope });
ctrl.$onInit();
});
});
});
I think it's not a good pattern to access directly window or document object from the component code.
The better way, as I see, is to put window in a separate service and then just use a dependency injection to put the service into component.
Check this for example.
Then you can spy the methods of the service, or just inject a mock service in your tests.
I have 2 classes, MyService and FooComponent as shown below:
class MyService {
getStuff(): Observable<Any> () {
//Implementation that may take sometime to return
}
}
class FooComponent {
private myServiceSubscription: Subscription;
public FooModel : MyDateType;
constructor(private myService: MyService){
this.FooModel = null;
this.myServiceSubscription = null;
}
Init() {
this.myServiceSubscription = this.myService.getStuff().subscribe(response => {
//Construct this.FooModel from response;
}
}
Done() {
if (this.myServiceSubscription !== null) {
myServiceSubscription.unsubscribe();
}
}
}
The FooComponent takes an instance of MyService and calls getStuff method in its Init method. Note the subscription call using RxJs module. This means the Init method returns before the subscribe event fires. Based on this code, I have following unit test (using Jasmine framework):
describe('Component: Foo', () => {
it('Load Foo Model', (() => {
var myService = new MyService();
var instance = new FooComponent(myService);
instance.Init();
if (instance.FooModel == null)
{
fail("FooModel is null even after calling Init() method.");
}
}));
});
This test always fails because the test does not wait until subscribe event is fired in the FooComponent's Init method and hence FooComponent instance FooModel is not populated.
What can I do so that some how the test waits for the subscribe event to fire and FooModel is populated?
You should use a stub as #Jin proposed. Most simple way:
fdescribe('Component: Foo', () => {
it('Load Foo Model', (() => {
var myService = jasmine.createSpyObj('myService', ['getStuff']);
myService.getStuff.and.callFake(() => {
return Observable.of(/* response */);
});
var instance = new FooComponent(myService);
instance.Init();
if (instance.FooModel == null)
{
fail("FooModel is null even after calling Init() method.");
}
}));
});
if you return value from getStuff synchronously then callback in Init will be called also syncronously and no need in using timeout or something.
I am new to angular and I am struggling to see how I should create a particular promise. I am also using typescript for coding.
I need to call a web service to authenticate this has to be done in 2 steps.
Step 1
Request an authentication key
Step 2
Process logon details with authentication key and return to service to retrieve the logged on user or error if incorrect
So I have created an angular service called AuthenticationService
like below (this of course doesn't work)
export class Authentication
{
$inject: string[] = ['$http'];
public Authenticate( userName: string, password: string ): ng.IHttpPromise<ApiModel.ApiResult<ApiModel.AuthenticatedUser>>
{
$http.post( "/Account/AuthenticationKey", null )
.then<ApiModel.ApiResult<string>>( result =>
{
var strKey = userName + password; //TODO: Put correct code here!
var promiseToReturn = $http.post( "/Account/Authenticate", { key: strKey })
.then<ApiModel.ApiResult<ApiModel.AuthenticatedUser>>( result =>
{
return result;
});
});
}
}
How do I go about returning a promise with the correct return type from the authentication method that returns the second result?
I can tell you that in JavaScript as i am not conversant with typescript. The idea is to create your own promise and resolve it whenever you want. Basically
var autenticate=function(user,password) {
var defer=$q.defer();
$http.post( "/Account/AuthenticationKey", null )
.then(function(data) {
//do something on data
$http.post( "/Account/Authenticate", { key: strKey })
.then(function(result) {
defer.resolve(result)
});
})
return defer.promise;
}
A then function should always either return another promise or a return value. The final then function should return a value which will get propagated to the top.
The relevant documentation can be found here :
https://github.com/kriskowal/q
NOTE : Angular's promise implementation is based on kriskowal's q.
This is the relevant section of the documentation :
If promiseMeSomething returns a promise that gets fulfilled later with
a return value, the first function (the fulfillment handler) will be
called with the value. However, if the promiseMeSomething function
gets rejected later by a thrown exception, the second function (the
rejection handler) will be called with the exception.
In your case, you should do something like
export class Authentication
{
$inject: string[] = ['$http'];
public Authenticate( userName: string, password: string ): ng.IHttpPromise<ApiModel.ApiResult<ApiModel.AuthenticatedUser>>
{
return $http.post( "/Account/AuthenticationKey", null )
.then<ApiModel.ApiResult<string>>( result =>
{
var strKey = userName + password; //TODO: Put correct code here!
return $http.post( "/Account/Authenticate", { key: strKey })
.then<ApiModel.ApiResult<ApiModel.AuthenticatedUser>>( result =>
{
return result;
});
});
}
}
Notice the two returns before the $http.posts that you are calling. All $http methods in Angular return a promise, which means you dont need to explicitly create another promise.