Angular unit test fails because of component in constructor. - javascript

I have a constructor that uses this:
constructor(
public core: CoreComponent,
) {
//
}
But when i try to test these components with a testcase like this:
describe('CoreSubMenuComponent', () => {
let component: CoreSubMenuComponent;
let fixture: ComponentFixture<CoreSubMenuComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [
TestingModule.forRoot(),
]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(CoreSubMenuComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
I get a null injector error:
Error: StaticInjectorError(DynamicTestModule)[CoreSubMenuComponent -> CoreComponent]:
StaticInjectorError(Platform: core)[CoreSubMenuComponent -> CoreComponent]:
NullInjectorError: No provider for CoreComponent!
It is imported into the testingModule so that isn't the issue. I also tried adding it to the providers array. but that just moves it to the angular renderer which can't be added to the providers array. I also added an #injectable() tag before the #component tag in the CoreComponent but it doesn't change my tests failing.
I tried getting it with #host() but it throws an error. I also tried to get it with the Injector but that also throws an error. These are 4 components that use the CoreComponent as a var to get some info from them but they make the tests fail. All my other tests are pass just fine with over 80 tests. Just these 4 won't work because they try to get properties that are defined in the CoreComponent.
Does someone know how i could reference this component with an instance so that my tests will pass?

To fix this error use #host en #optional decorators. this tells the compiler to look for it but not require if it isn't provided:
constructor(
#Host() #Optional() public core: CoreComponent,
) {
//
}

Related

Jasmine failure: Error: <spyOn> : could not find an object to spy upon for postSkillforMember()

I am trying to test a method which does not return nothing. The method calls a service to perform a Http POST. I have mocked my service and set it as a parameter within SpyOn. The second parameter in the SpyOn call is the name of the method that contains the post operation. In this case, the method name is postSkillforMember. When I run ng test, Jasmine spec list failures throws the following error:
Jasmine Spec List | Failures:
AddSkillModalComponent > Testing addSkill() success
Error: <spyOn> : could not find an object to spy upon for postSkillforMember()
What I am doing wrong? This is the method in my component.ts that I want to test:
Method to test:
addSkill(): void {
this.successCallback=(skill: any) => {
this.showAlert = false;
this.activeModal.close(skill);
};
this.errorCallback=(error: any)=>{
this.showAlert=true;
};
const skillFormBody = {
skill: {
"id": this.itemFormGroup.get("skill")?.value.id,
"skill": ""
},
skillLevel: {
"id": this.itemFormGroup.get("skillLevel")?.value.id,
"skillLevel": ""
}
}
this._skillAddService.postSkillforMember(
this.memberId,
skillFormBody,
this.successCallback,
this.errorCallback
);}
My spec ts mocks the service within describe:
describe in add-skill-modal.component.ts:
describe('AddSkillModalComponent', () => {
let component: AddSkillModalComponent;
let fixture: ComponentFixture<AddSkillModalComponent>;
let mockSkillAddService: jasmine.SpyObj<SkillAddService>;}
And, finally, this is the unit test I wrote for my method:
it in add-skill-modal.component.ts:
it('Testing addSkill() success', () => {
spyOn(mockSkillAddService, 'postSkillforMember');
component.addSkill();
expect(component.showAlert).toBeFalsy();
});
I really appreciate your help. Thank you in advance.
Can you show your TestBed.configureTestingModule({?
Make sure you create a spyObj for SkillAddService and add it to the providers.
beforeEach(waitForAsync(() => {
// make sure you create this spy object with postSkillforMember public method to be mocked
mockSkillAddService = jasmine.createSpyObj<SkillAddService>('SkillAddService', ['postSkillforMember']);
TestBed.configureTestingModule({
declarations: [...],
// provide the mock instead of the real one
providers: [{ provide: SkillAddService, useValue: mockSkillAddService }],
}).compileComponents();
}));
it('Testing addSkill() success', () => {
// no need to spy anymore
// spyOn(mockSkillAddService, 'postSkillforMember');
component.addSkill();
expect(component.showAlert).toBeFalsy();
// can do:
expect(mockSkillAddService.postSkillforMember).toHaveBeenCalled();
});

Angular 9 unit test external function referring property inside service gives errors

I have a service to run unit tests as follows. It has two helper functions which use service params as follows.
Here is the service.ts file
export function storeToStorage<T>(key: string, item: T): void {
this.storageService.libLocalStorage( 'set' ,key, item )
}
export function getFromStorage<T>(key: string): T {
return this.storageService.libLocalStorage( 'get' ,key )
}
#Injectable({
providedIn: 'root'
})
export class TableColumnService {
...
constructor(private storageService: LocalStorageService) {
localStorage.removeItem('KEY');
this._initializeColumns();
}
...
}
Here Local storage service is service implemented separately for managing storage of the application. And application is running with out any error.
My implementation for service.spec.ts as follows
describe('TableColumnService', () => {
let service: TableColumnService;
let localStorageService: LocalStorageService;
beforeEach(async (() => {
TestBed.configureTestingModule({
imports: [TestSharedModule],
providers: [LocalStorageService]
});
service = TestBed.inject(TableColumnService);
localStorageService = TestBed.inject(LocalStorageService);
}));
it('should be created', () => {
expect(service).toBeTruthy();
});
});
I am getting following error when running
Chrome Headless 90.0.4430.212 (Mac OS 10.15.7) TableColumnService should be created FAILED
Failed: Cannot read property 'storageService' of undefined
at <Jasmine>
I tried to spy external functions as follows. But giving errors
beforeEach(async (() => {
TestBed.configureTestingModule({
imports: [TestSharedModule],
providers: [LocalStorageService]
});
service = TestBed.inject(TableColumnService);
localStorageService = TestBed.inject(LocalStorageService);
spyOn(storeToStorage, 'storeToStorage').and.returnValue({});
spyOn(getFromStorage, 'getFromStorage').and.returnValue({});
}));
It gives following error on spy functions
Argument of type '{}' is not assignable to parameter of type 'never'
I reckon you're not spying correctly; it should be something like below and yes, you have to make stroage service public in ts file. If you don't want to make it public you can also create a stub for this service in your spec file.
spyOn(service.storageService, 'storeToStorage').and.returnValue({});

Testing: spyOn Helper class in angular

Is it possible to spyOn helper class? In the below code, StatefulPatternService.init() is calling a WebSocketHelper.
I would like to spyOn WebSocketHelper and mock the subscribeFn
export class WebSocketHelper{
private url: string;
constructor(url){
this.url = url;
init();
}
init(){
// init websocket & other login
}
}
#Injectable({
providedIn: 'root'
})
export class StatefulPatternService {
constructor(){}
private callback(_data){ }
init(){
let wsHelper = new WebSocketHelper('/subscribe/topic'); // <-- How to spyOn???
wsHelper.subscribeFn = this.callback;
// ...
}
}
If spyOn won't be possible, then how it can be re-written so that this test can be covered?
Your challenge will be getting a hold of 'wsHelper' in order to spy on it. One thought: can you refactor to make wsHelper a class-scope variable instead? Then you could spyOn it when you get the service in the test suite, for example, something like:
service = TestBed.get(StatefulPatternService);
let spy = spyOn(service.wsHelper, 'subscribeFn');
Update
From the comments to my answer it looks like what you are really trying to do is verify that the constructor was called with the proper url. Since you are saving that in a class variable, there should be no need to spy on the constructor, but rather just test the value of the saved variable. As I mentioned in the comments, to do this you will need two things: to make wsHelper a class level variable, and to add a method on the WebSocketHelper class that returns the value of the private variable 'url' so you can test it. I've set up a stackblitz to demonstrate what I'm talking about here: STACKBLITZ Here is a snippet from that stackblitz:
describe('MyService', () => {
let myService: StatefulPatternService;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [/* any imports needed */],
providers: [ StatefulPatternService ]
});
myService = TestBed.get(StatefulPatternService);
});
it('should be createable', () => {
expect(myService).toBeTruthy();
});
it('should construct wsHelper properly', () => {
myService.init();
expect(myService.wsHelper.get_url()).toEqual('/subscribe/topic');
})
});

Unable to get property 'subscribe' of undefined or null reference in ActivatedRoute

I want to know how to mock ActivatedRoute url.
I am getting current url by using ActivatedRoute
this.activatedRoute.url.subscribe(url => {
this.isEdit = false;
if (url[0].path === 'updatecoc') {
this.isEdit = true;
}
});
So I want to mock url in ActivatedRoute
I have tried this
let fakeActivatedRoute = new MockActivatedRoute();// MockActivatedRoute I have implemented this class from MockActivatedRoute class
fakeActivatedRoute.parent = new MockActivatedRoute();
let urlSegment: UrlSegment[] = [];
urlSegment.push({ path: "updatecoc", parameters: {} });
fakeActivatedRoute.url = Observable.of(urlSegment);
TestBed.configureTestingModule({ { provide: ActivatedRoute, useValue: fakeActivatedRoute }})
But I am getting error:
unable to get property 'subscribe' of undefined or null reference
I don't know where I am missed. How can I resolve this issue?
I have a better solution for you :
import { RouterTestingModule } from '#angular/router/testing';
TestBed.configureTestingModule({
imports: [RouterTestingModule],
// ...
})
.compileComponents();
This will mock your whole routing module, now you can inject your dummy mock into your providers and spy on the functions like so
providers: [{provide: ActivatedRoute, useValue: {}}]
And when you test a function calling, let's say, myMock (I know it's not in it, it's for the example) :
const mock = TestBed.get(ActivatedRoute);
spyOn(mock, 'myMock').and.returnValue(/* what you want */);
// ...
expect(mock.myMock).toHaveBeenCalledWith(/* params here */);
EDIT I quickly looked at what url is made of, here is your mock :
mock.url = jasmine
.createSpy()
.and
.returnValue(new BehaviorSubject({
path: 'yout-mocked-path',
parameters: {/* your mocked parameters */}
})));

Unit tests for firebase add using angular 2

Does anyone know how to do a basic unit test using angular 2 to test a basic firebase add item.
I'm using typescript instead of basic JavaScript for my code
This is what I'm testing:
export class AppComponent {
ref: Firebase;
refUsers: Firebase;
refProfiles: Firebase;
constructor(){
this.ref = new Firebase("https://markng2.firebaseio.com");
this.refUsers = new Firebase("https://markng2.firebaseio.com/users");
this.refProfiles = new Firebase("https://markng2.firebaseio.com/profiles");
}
public addUser(newUser: Object): void{
this.refUsers.push(newUser, ()=>{
});
}
}
This is my current test:
import {it, iit, describe, expect, inject, injectAsync, beforeEachProviders, fakeAsync, tick } from 'angular2/testing';
import { AppComponent } from '../app/app';
describe('AppComponent', () => {
it('saves an item to Firebase', () => {
let refUsers = new Firebase('');
let service = new AppComponent();
spyOn(service.refUsers, 'push');
service.addUser({ item: true });
expect(service.refUsers.push).toHaveBeenCalled();
})
});
This is the error I'm getting when I run that test:
Three steps to begin testing.
Setup your testing environment. The Angular 2 docs have a great guide on doing so.
Write your code.
Write the test.
Let's say you create a class called DataService:
/// <reference path="typings/firebase/firebase.d.ts" />
export class DataService {
ref: Firebase;
constructor(theRef: Firebase) {
this.ref = theRef;
}
add(object: any) {
this.ref.push(object);
}
}
To test it, you can import DataService and use Jasmine methods to test that the add method.
import {DataService} from './data-service';
describe('DataService', () => {
it('saves an item to Firebase', () => {
let ref = new Firebase('');
let service = new DataService(ref);
// create a spy to use if push is called
spyOn(service.ref, 'push');
service.add({ item: true });
// expect that push was called
expect(service.ref.push).toHaveBeenCalled();
})
});
The key to testing Firebase methods is just to spy on them. You don't need to test that Firebase works, just that your code calls Firebase properly.
The problem here is that you're using the full Firebase SDK in your unit tests. Ideally you'd want to use a mocked library, so you can create a mock for whatever functionality you need from the Firebase SDK.

Categories

Resources