spyOn could not find an object to spy upon for start() - javascript

I am using angular-cli testing framework.
inside my component , I have used 'ng2-slim-loading-bar' node module.
submit(){
this._slimLoadingBarService.start(() => {
});
//method operations
}
Now when I am testing this component, I have applied spyOn this service as :
beforeEach(() => {
let slimLoadingBarService=new SlimLoadingBarService();
demoComponent = new DemoComponent(slimLoadingBarService);
TestBed.configureTestingModule({
declarations: [
DemoComponent
],
providers: [
{ provide: SlimLoadingBarService, useClass: SlimLoadingBarService}
],
imports: [
SharedModule
]
});
});
it('should pass data to servie', () => {
spyOn(slimLoadingBarService,'start').and.callThrough();
//testing code,if I remove the above service from my component, test runs fine
});
but its not working.
It throws below error:
spyOn could not find an object to spy upon for start()

Declaring slimLoadingBarService with let, you are constraining its scope to the beforeEach callback scope. Declare it with var, or better, declare it after the proper describe() block and set its content within beforeEach callback function:
describe("some describe statement" , function(){
let slimLoadingBarService = null;
beforeEach( () => {
slimLoadingBarService=new SlimLoadingBarService();
});
it('should pass data to service', () => {
spyOn(slimLoadingBarService,'start').and.callThrough();
//testing code,if I remove the above service from my component, test runs fine
});
});

it's due to non declaration in beforeEach
updated syntax after angular 10
beforeEach(() => {
slimLoadingBarService = TestBed.inject(SlimLoadingBarService);
});
before angular 10
beforeEach(() => {
slimLoadingBarService = TestBed.get(SlimLoadingBarService);
});

Related

How to write Test cases for below angular method

I have created a component that opens my custom type dialog, I just want to create Jasmine unit test cases for this method.
export class OpenPopUpComponent implements OnInit {
constructor(public dialog:NewCustomDialog) {}
ngOnInit() {
}
openModel(){
this.dialog.open(NewComponent,<NewCustomDialogConfig>{
size: 'double',
data: {
title: 'New Dialog'
}
});
}
}
You will not test the dialog itself. What you need to do is to mock the NewCustomDialog and provide it as injected.
In your spec.ts
beforeEach(() => {
const spy = jasmine.createSpyObj('NewCustomDialog', ['open']);
TestBed.configureTestingModule({
// Provide (spy) dependency
providers: [
{ provide: NewCustomDialog, useValue: {newCustomDialogSpy} }
]
});
// Inject both the service-to-test and its (spy) dependency
masterService = TestBed.get(MasterService);
valueServiceSpy = TestBed.get(ValueService);
});
Then you can check that the spy has been called with parameters (the ones you expect).
The intension of the unit test is to test the feature of component itself and not to start testing the features which is outside the scope of component which is to be tested. So,
you do not need to test dialog.open as this should be tested in unit test of NewCustomDialog itself.
start by creating a Stub which you can use as a placeholder for NewCustomDialog, such as
export class NewCustomDialogStub{
open(){ return null; }
close(){ return null; }
// and similar dummy methods which is required by "OpenPopUpComponent"
}
Inject this stub as useClass in providers as below:
export class NewCustomDialogStub{
open(){ return null; }
close(){ return null; }
// and similar dummy methods which is required by "OpenPopUpComponent"
}
describe('OpenPopUpComponent', () => {
let component: OpenPopUpComponent;
let fixture: ComponentFixture<OpenPopUpComponent>;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [],
declaration: [OpenPopUpComponent],
providers: [
{ provide: NewCustomDialog, useClass: NewCustomDialogStub }
]
}).compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(OpenPopUpComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should be defined',()=>{
expect(component).toBeDefined();
})
it('should call "open" method of dialog on calling openModel() ',()=>{
spyon(component.dialog,'open').and.callThrough();
component.openModel();
expect(component.dialog.open).toHaveBeenCalled();
})
})
This is very basic testing but if you want to know more about writing tests , you can refer to this series of articles where I have covered almost all basic testing scenarios . Check the bottom of article for all links. The one which I used here is this one

AngularJS unit test for service : $urlMatcherFactory throws unknown error

I have few questions with to write a proper unit test for a service using jasmine as framework and karma as test runner.
here is what i implemented in example-service.js:
export default class ExampleService {
constructor($resource, $http, $urlMatcherFactory) {
'ngInject';
this.$resource = $resource;
this.$http = $http;
this.$urlMatcherFactory = $urlMatcherFactory;
}
exampleMethodOne() {
//some code lines
}
exampleMethodTwo() {
//some code lines
}
}
ExampleService.selector = 'myExampleService';
Here what i wrote in my test example-service.test.js
let myExampleService, $httpBackend, $urlMatcherFactory;
beforeEach(() => {
angular
.module('exampleApp', ['ngResource'])
.service(ExampleService.selector, ExampleService);
angular.mock.module('exampleApp');
});
beforeEach(inject((_myExampleService_, _$httpBackend_,
_$urlMatcherFactory_) => {
myExampleService = _myExampleService_;
$httpBackend = _$httpBackend_;
$urlMatcherFactory = _$urlMatcherFactory_;
}));
i have imported the angular-mocks.js, angular-resource.js and example-service.js
when i try this scenario the console will throw a Error: [$injector:unpr] Unknown provider: $urlMatcherFactoryProvider <- $urlMatcherFactory <- myExampleService error.
please help me to solve this.
I suppose $urlMatcherFactory refers to UI router service, and UI Router wasn't loaded.
It's not recommended to use real router in unit tests because it provides extra moving parts and breaks isolation between units. As a rule of thumb, every unit but a tested one should be mocked.
$urlMatcherFactory stub methods have to be configured to return expected values. Since they are used during ExampleService construction, they should be configured prior to service instantiation:
let removeAllUrlMock;
beforeEach(() => {
removeAllUrlMock = jasmine.createSpyObj('removeAllUrl', ['format']);
$urlMatcherFactory = jasmine.createSpyObj('urlMatcherFactory', ['compile']);
$urlMatcherFactory.compile.and.returnValue(removeAllUrlMock);
angular
.module('exampleApp', ['ngResource'])
.service(ExampleService.selector, ExampleService);
angular.mock.module('exampleApp');
angular.mock.module({ $urlMatcherFactory });
});
This can be tested with:
expect($urlMatcherFactory.compile).toHaveBeenCalledOnce();
expect($urlMatcherFactory.compile).toHaveBeenCalledWith('../api/v1/user/{Id}/remove/all');
expect(myExampleService.removeAllUrl).toBe(removeAllUrlMock);
Then specific removeAllUrl calls can be mocked and tested when used:
removeAllUrlMock.format.and.returnValue('../api/v1/user/foo/remove/all');
myExampleService.removeProduct('foo');
expect(removeAllUrlMock.format).toHaveBeenCalledOnce();
expect($urlMatcherFactory.compile).toHaveBeenCalledWith(
jasmine.objectContaining({ id: 'foo' })
);
Since $urlMatcherFactory is utility service that doesn't provide much moving parts and supposed to be predictable, it can alternatively be imported and used directly, without UI router module:
import { UrlMatcherFactory } from '#uirouter/angularjs';
...
beforeEach(() => {
angular
.module('exampleApp', ['ngResource'])
.service(ExampleService.selector, ExampleService);
angular.mock.module('exampleApp');
angular.mock.module(($provide) => {
$provide.service('$urlMatcherFactory', UrlMatcherFactory });
});
});
Then it's just has to be spied:
spyOn(myExampleService, 'removeAllUrl').and.callThrough();
myExampleService.removeProduct('foo');
expect(removeAllUrlMock.format).toHaveBeenCalledOnce();
expect($urlMatcherFactory.compile).toHaveBeenCalledWith(
jasmine.objectContaining({ id: 'foo' })
);

dblclick handler not registered by angular's test suite

I have an Angular 4 component that listens to a dblclick event. This works fine in the browser:
acticle.component.html
<div (dblclick)="openArticle()">
<!-- stuff -->
</div>
acticle.component.ts
#Component({
selector : 'app-article',
templateUrl: 'article.component.html',
styleUrls : ['article.component.scss'],
})
export class ArticleComponent implements OnInit {
// stuff
openArticle(): void {
// simplified code
this.openArticleInCs();
}
}
Now I am trying to test if the double click properly executes what happens inside openArticle(), like so:
describe('Component: ArticleComponent', () => {
// some variable declarations here
beforeEach(() => {
// some variable definitions here
TestBed.configureTestingModule({
declarations: [/* some declarations here */],
imports: [/* some modules here */],
providers: [/* some providers here */]
});
fixture = TestBed.createComponent(ArticleFactoryComponent);
comp = fixture.componentInstance;
fixture.detectChanges();
de = fixture.debugElement.query(By.directive(ArticleComponent));
el = de.nativeElement;
articleComponent = de.injector.get(ArticleComponent);
});
it('should open an article in CS on double click', () => {
articleComponent.openArticleInCue = jasmine.createSpy('openArticleInCue').and.returnValue(undefined);
articleComponent.openArticleInCs = jasmine.createSpy('openArticleInCs').and.returnValue(undefined);
de.triggerEventHandler("dblclick", new MouseEvent("dblclick"));
fixture.detectChanges();
expect(articleComponent.openArticleInCs).toHaveBeenCalled();
});
});
But the tests fail at
expect(articleComponent.openArticleInCs).toHaveBeenCalled();
Using the debugger I can see that triggerEventHandler works and inside that function Angular iterates though the defined event handlers for this component. However, the event handlers are empty! So it seems to me Angular's test suite does not understand that I have a dblclick handler there (if i am not mistaken on my presumption that is).

Map Object in .run block breaks my unit test: ReferenceError: Can't find variable in app/scripts/app.js

I set up a mock backend in my AngularJS app using ngMockE2E as I am waiting for an API. In my .run block I declare my various $httpBackend.whenGET and $httpBackend.whenPost methods and I have a variable to return data from these methods which is a JS Map() object... like so (not all code is here)
.run(function ($httpBackend) {
var fakeData = new Map();
fakeData.set(/*stuff goes here*/);
$httpBackend.whenGET('/geturl').respond(function () {
// do stuff and return parts of the Map()
});
$httpBackend.whenPOST('/posturl').respond(function (method, url, data) {
// do stuff and return parts of the Map()
});
Now I have set up my first unit test to test a service that uses these mock backends...
describe('Service: MyAppService: ', function () {
// load the controller's module
beforeEach(module('MyApp'));
var MyAppService, $httpBackend;
describe('MyAppService Service should be created', function () {
beforeEach(inject(function($injector) {
MyAppService = $injector.get('MyAppService');
$httpBackend = $injector.get('$httpBackend');
$httpBackend.when('GET', '/geturl').respond(['A', 'B', 'C', 'D', 'E']);
}));
afterEach(function() {
$httpBackend.verifyNoOutstandingExpectation();
$httpBackend.verifyNoOutstandingRequest();
});
it('test all is well before real tests', function () {
expect(true).to.equal(true);
});
});
});
However with no real tests in my UnitTest I am already getting the following error: ReferenceError: Can't find variable: Map in app/scripts/app.js
What must I add to the Unit Test to prevent this error? Many thanks in advance.
PhantomJS does not support ES5 and ES6. Map is ES5. RE https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map
You need to add es6-shim (includes es5) that implements it, see npm package: https://www.npmjs.com/package/es6-shim
After installing, reference it in karma.conf.js in files section.
files: [ 'app/bower_components/es6-shim/es6-shim.js' ]

Mocking custom provider injected into provider when unit testing Angular in Jasmine

I'm unit testing a provider in Jasmine, which relies on another provider. There's no configuration associated with this provider. When mocking a provider, I've read you're supposed to use something like
beforeEach(module(function ($provide) {
mockInjectedProvider = { };
$provide.value('injected', mockInjectedProvider );
}));
which works fine for me when injecting a custom provider into a service. When injecting them into a provider it doesn't work though. The code doesn't fail, but what gets executed when testing is the actual provider, not the mocked one. Abstracted example below.
var mockInjectedProvider;
beforeEach(function () {
module('myModule');
});
beforeEach(module(function ($provide) {
mockInjectedProvider = {
myFunc: function() {
return "testvalue"
}
}
};
$provide.value('injected', mockInjectedProvider );
}));
beforeEach(inject(function (_base_) {
baseProvider = _base_;
}));
it("injectedProvider should be mocked", function () {
var resultFromMockedProvider = baseProvider.executeMyFuncFromInjected();
expect(resultFromMockedProvider).toEqual("testvalue");
}); // Here instead of using my mock it executes the actual dependency
In the $provide.value statement I've tried including both injected and injectedProvider, as well as using $provide.provider and mocking a $get function on it but nothing seems to work. I just can't get it to mock away the actual provider. Abstracted base provider looks like this.
(function (ng, module) {
module.provider("base",
["injectedProvider", function (injectedProvider) {
this.executeMyFuncFromInjected= function() {
return injectedProvider.myFunc(); // let's say this returns "realvalue"
}
this.$get = function () {
return this;
};
}]
);
})(window.angular, window.angular.module("myModule"));
Everything in my code is working except the Jasmine mocking.
In this case is better to just mock the return value instead of the provider.
var mockInjectedProvider;
beforeEach(function () {
module('myModule');
});
beforeEach(inject(function (_injected_) {
spyOn(_injected_, "myFunc").and.returnValue("testvalue");
}));
beforeEach(inject(function (_base_) {
baseProvider = _base_;
}));
it("injectedProvider should be mocked", function () {
var resultFromMockedProvider = baseProvider.executeMyFuncFromInjected();
expect(resultFromMockedProvider).toEqual("testvalue");
}); // Here instead of using my mock it executes the actual dependency

Categories