could you please tell me why I am getting this error Cannot read property 'getData' of undefined
I am trying to test my service when I try to run my test it gives me above error
here is my code
https://stackblitz.com/edit/angular-testing-w9towo?file=app%2Fapp.service.spec.ts
import { TestBed, ComponentFixture, async, inject } from '#angular/core/testing';
import {HttpClientTestingModule, HttpTestingController} from '#angular/common/http/testing';
import {Posts} from './post.interface';
import { AppService } from './app.service';
describe('AppService', () => {
let service:AppService,
httpMock:HttpTestingController;
beforeEach(() => {
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [HttpClientTestingModule],
providers: [AppService]
}).compileComponents();
service =TestBed.get(AppService);
httpMock =TestBed.get(HttpTestingController);
}));
afterEach(()=>{
httpMock.verify();
})
})
describe('Service is truty', () => {
it('should return an Observable<User[]>', () => {
const dummyUsers :Posts[]= [{
userId: 10,
id: 10,
title: 'post',
body: 'post'
}];
service.getData().subscribe(users => {
console.log(users)
expect(users.length).toBe(1);
expect(users).toEqual(dummyUsers);
});
const req= httpMock.expectOne('https://jsonplaceholder.typicode.com/posts')
expect(req.method).toBe('GET');
req.flush(dummyUsers);
});
})
})
After trying more I am getting Cannot read property 'getData' of undefined
Why do you use nested beforeEach() and compileComponents()? Your beforeEach() statement should look like this:
beforeEach(() => {
TestBed.configureTestingModule({
imports: [HttpClientTestingModule],
providers: [AppService]
});
service = TestBed.get(AppService);
httpMock = TestBed.get(HttpTestingController);
});
Try this:
import { TestBed, ComponentFixture, async, inject } from '#angular/core/testing';
import {HttpClientTestingModule, HttpTestingController} from '#angular/common/http/testing';
import {Posts} from './post.interface';
import {HttpModule} from '#angular/http';
import { AppService } from './app.service';
describe('AppService', () => {
let service:AppService,
httpMock:HttpTestingController;
let fixture;
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [HttpClientTestingModule,
HttpModule],
providers: [
AppService
]
}).compileComponents().then( () => {
service = TestBed.get(AppService);
httpMock =TestBed.get(HttpTestingController);
})
}));
afterEach(()=>{
httpMock.verify();
})
describe('Service is truty', () => {
it('should return an Observable<User[]>', () => {
const dummyUsers :Posts[]= [{
userId: 10,
id: 10,
title: 'post',
body: 'post'
}];
service.getData().subscribe(users => {
console.log(users)
expect(users.length).toBe(1);
expect(users).toEqual(dummyUsers);
});
const req= httpMock.expectOne('https://jsonplaceholder.typicode.com/posts')
//expect(req.method).toBe('GET');
req.flush(dummyUsers);
});
})
})
Live Demo:
https://stackblitz.com/edit/angular-testing-oqrffm?file=app%2Fapp.service.spec.ts
Related
I have the following problem while running ng test in Angular 12:
NullInjectorError: R3InjectorError(DynamicTestModule)[BaseURL ->
BaseURL]: NullInjectorError: No provider for BaseURL! error
properties: Object({ ngTempTokenPath: null, ngTokenPath: [ 'BaseURL',
'BaseURL' ] }) NullInjectorError:
R3InjectorError(DynamicTestModule)[BaseURL -> BaseURL]:
NullInjectorError: No provider for BaseURL!
at NullInjector.get (http://localhost:9876/karma_webpack/webpack:/node_modules/#angular/core/ivy_ngcc/fesm2015/core.js:11101:1)
at R3Injector.get (http://localhost:9876/karma_webpack/webpack:/node_modules/#angular/core/ivy_ngcc/fesm2015/core.js:11268:1)
at R3Injector.get (http://localhost:9876/karma_webpack/webpack:/node_modules/#angular/core/ivy_ngcc/fesm2015/core.js:11268:1)
at NgModuleRef$1.get (http://localhost:9876/karma_webpack/webpack:/node_modules/#angular/core/ivy_ngcc/fesm2015/core.js:25332:1)
at Object.get (http://localhost:9876/karma_webpack/webpack:/node_modules/#angular/core/ivy_ngcc/fesm2015/core.js:25046:1)
at lookupTokenUsingModuleInjector (http://localhost:9876/karma_webpack/webpack:/node_modules/#angular/core/ivy_ngcc/fesm2015/core.js:3342:1)
at getOrCreateInjectable (http://localhost:9876/karma_webpack/webpack:/node_modules/#angular/core/ivy_ngcc/fesm2015/core.js:3454:1)
at ɵɵdirectiveInject (http://localhost:9876/karma_webpack/webpack:/node_modules/#angular/core/ivy_ngcc/fesm2015/core.js:14737:1)
at NodeInjectorFactory.MenuComponent_Factory [as factory] (ng:///MenuComponent/ɵfac.js:5:7)
at getNodeInjectable (http://localhost:9876/karma_webpack/webpack:/node_modules/#angular/core/ivy_ngcc/fesm2015/core.js:3549:1)
Error: Expected undefined to be truthy.
at
at UserContext. (http://localhost:9876/karma_webpack/webpack:/src/app/menu/menu.component.spec.ts:46:23)
at ZoneDelegate.invoke (http://localhost:9876/karma_webpack/webpack:/node_modules/zone.js/fesm2015/zone.js:372:1)
at ProxyZoneSpec.onInvoke (http://localhost:9876/karma_webpack/webpack:/node_modules/zone.js/fesm2015/zone-testing.js:287:1)
The code for the spec.ts is:
import { ComponentFixture, TestBed } from '#angular/core/testing';
import { MenuComponent } from './menu.component';
import { DishService } from '../services/dish.service';
describe('MenuComponent', () => {
let component: MenuComponent;
let fixture: ComponentFixture<MenuComponent>;
const mockDishService = {
getDishes: () => {
return {
id: '000',
name: 'nnnnn'
}
}
}
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [],
declarations: [ MenuComponent ],
providers: [
{ provide: DishService, useValue: mockDishService },
]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(MenuComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
The code of the component is:
import { Component, OnInit, Inject } from '#angular/core';
import { Dish } from '../shared/dish';
import { DishService } from '../services/dish.service';
import { flyInOut, expand } from '../animations/app.animation';
#Component({
selector: 'app-menu',
templateUrl: './menu.component.html',
styleUrls: ['./menu.component.scss'],
// tslint:disable-next-line:use-host-property-decorator
host: {
'[#flyInOut]': 'true',
'style': 'display: block;'
},
animations: [
flyInOut(),
expand()
]
})
export class MenuComponent implements OnInit {
dishes!: Dish[];
errMess: string;
constructor(private dishService: DishService,
#Inject ('BaseURL') public baseURL) { }
ngOnInit(): void {
this.dishService.getDishes().subscribe((dishes => this.dishes = dishes), errMess => this.errMess = <any>errMess);
}
}
I'm using the baseURL to obtain the information for a db.json file while running json-server --watch db.json to simulate that I get the information from the server, so I have a shared .ts file named baseurl.ts with the following code:
export const baseURL = 'http://localhost:3000';
And in the app.module.ts I import the const
import { baseURL } from './shared/baseurl';
and I add it as a provider in the same app.module.ts file:
providers: [
{ provide: 'BaseURL', useValue: baseURL }
],
I will appreciate someone is help on this error
Issue 1: NullInjectorError
This error message shows as your DishComponent requires BaseURL to be injected [#Inject ('BaseURL')].
NullInjectorError: R3InjectorError(DynamicTestModule)[BaseURL -> BaseURL]: NullInjectorError: No provider for BaseURL!
Solution for Issue 1
Ensure that you add BaseURL in providers section for unit testing.
const baseUrl = "your Base URL";
beforeEach(async () => {
await TestBed.configureTestingModule({
...
providers: [
{ provide: DishService, useValue: mockDishService },
{ provide: 'BaseURL', useValue: baseUrl },
]
})
.compileComponents();
});
Issue 2: Return wrong value for mockDishService.getDishes()
From MenuComponent, it is expected that the dishService.getDishes() return the value of Observable<Menu[]> or Observable<any[]> type.
dishes!: Dish[];
this.dishService.getDishes().subscribe((dishes => this.dishes = dishes), errMess => this.errMess = <any>errMess);
While the mockDishService.getDishes() returns the value of any or Menu type, which the data returned was unmatched.
const mockDishService = {
getDishes: () => {
return {
id: '000',
name: 'nnnnn',
};
},
};
Hence you will get this error message as below:
TypeError: this.dishService.getDishes(...).subscribe is not a function
Solution for Issue 2:
Ensure that the mockDishService.getDishes() returns value of Observable<Menu[]> or Observable<any[]> type to match with real dishService.getDishes().
import { of } from 'rxjs';
const mockDishService = {
getDishes: () => {
return of([
{
id: '000',
name: 'nnnnn',
},
]);
},
};
Sample Solution on StackBlitz
can anyone help me please? I'm trying to test a function that call a firebase functions, but when I call the main function and start to run a firebase functions, I got a error
err TypeError: Cannot read property 'emailPasswordLoginAsPromise' of null
i don't know what is happen, follow my code:
fdescribe('UserLoginContentComponent', () => {
let component: UserLoginContentComponent;
let fixture: ComponentFixture<UserLoginContentComponent>;
let loginComponent = new UserLoginContentComponent(null,null,null,null,null,null,null);
beforeAll(
async(() => {
TestBed.configureTestingModule({
imports: [
SharedModule,
AngularFireModule.initializeApp(environment.firebase),
RouterTestingModule,
BrowserAnimationsModule
],
declarations: [UserLoginContentComponent],
providers: [
AuthService,
AngularFireAuth,
AngularFirestore,
LogService,
LogPublishersService,
HttpClient,
HttpHandler
]
}).compileComponents();
fixture = TestBed.createComponent(UserLoginContentComponent);
component = fixture.componentInstance;
fixture.detectChanges();
spyOn(loginComponent, 'onSubmit').and.callThrough();
loginComponent.loginModel.email = 'correct email';
loginComponent.loginModel.password = 'correct password';
})
);
it('component should be create', () => {
expect(component).toBeTruthy();
});
it('Correct login',function(){
loginComponent.onSubmit().then((x) => {
console.log('ok:',x)
//expect something ..
}).catch((err) => {
console.log('err',err)
})
});
});
my Function that I'm calling:
onSubmit() {
//i'm setting my loginModel in the test with email and password
console.log('this.loginModel',this.loginModel)
return new Promise((res,rej) => {
this.authService.emailPasswordLoginAsPromise(this.loginModel).then(userCredential => {
// do something..
this.authService.createOrUpdateUserDataFirestore(userCredential, null, avaliacaoChecklist, null, null).then(s =>
//updating my user or create one
}).catch(e => {
//catch if this doesn't update or create
});
});
res('login OK')
}).catch(e => {
//open a diaglog if happen something wrong...
rej('login Fail')
});
})
}
in my authService, my emailloginasPromise is like that :
emailPasswordLoginAsPromise(login) {
return new Promise((resolveEPL, rejectEPL) => {
this.angularFireAuth.auth.signInWithEmailAndPassword(login.email, login.password)
.then(credential => {
this.updateUserWithAuth(credential.user);
resolveEPL(credential.user);
}).catch(e => {
console.error('emailPasswordLogin', e);
rejectEPL(e);
});
});
}
it's my first time with testing jasmine, I studied, but i don't know how I can solve this problem, how call a async func and getting the return.
i founded the problem, follow the fix:
The authService isn't provide when i'm creating a stance of my class, so now i'm using the component:
component = fixture.componentInstance;
with this component now I'm calling my method and all providers is working.
Follow my describe:
import { ComponentFixture, TestBed } from '#angular/core/testing';
import { SharedModule } from '../../shared/shared.module';
import { UserLoginContentComponent } from './user-login-content.component';
import { AngularFireModule } from '#angular/fire';
import { environment } from 'src/environments/environment';
import { RouterTestingModule } from '#angular/router/testing';
import { AuthService } from 'src/app/core';
import { AngularFireAuth } from '#angular/fire/auth';
import { AngularFirestore } from '#angular/fire/firestore';
import { LogService } from 'src/app/shared/logger/log.service';
import { LogPublishersService } from 'src/app/shared/logger/log-publishers.service';
import { HttpClient, HttpHandler } from '#angular/common/http';
import { BrowserAnimationsModule } from '#angular/platform-browser/animations';
fdescribe('UserLoginContentComponent', () => {
let component: UserLoginContentComponent;
let fixture: ComponentFixture<UserLoginContentComponent>;
beforeAll(function(){
TestBed.configureTestingModule({
imports: [
SharedModule,
AngularFireModule.initializeApp(environment.firebase),
RouterTestingModule,
BrowserAnimationsModule
],
declarations: [UserLoginContentComponent],
providers: [
AuthService,
AngularFireAuth,
AngularFirestore,
LogService,
LogPublishersService,
HttpClient,
HttpHandler
]
}).compileComponents();
fixture = TestBed.createComponent(UserLoginContentComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
});
and how to test this component?
I'm using the follow tests:
it('COrrect login',(async(done) => {
component.loginModel.email = 'correctemail#gmail.com';
component.loginModel.password = 'correctpassword';
await component.onSubmitTest().then((x) => {
expect(x).toBe('login OK');
});
done();
}));
it('Wrong login (email)',(async(done) => {
component.loginModel.email = 'wrongemail#gmail.com';
component.loginModel.password = 'correctpassword';
await component.onSubmitTest().then(() => {})
.catch((err) => {
expect(err).toBe('login Fail');
})
done();
}));
My class follow:
onSubmitTest() {
return new Promise((res,rej) => {
this.authService.emailPasswordLoginAsPromise(this.loginModel).then(() => {
res('login OK')
}).catch(e => {
rej('login Fail')
});
})
}
and my authService:
emailPasswordLoginAsPromise(login) {
return new Promise((resolveEPL, rejectEPL) => {
this.angularFireAuth.auth.signInWithEmailAndPassword(login.email, login.password)
.then(credential => {
resolveEPL(credential.user);
}).catch(e => {
rejectEPL(e);
});
});
}
And now all my testing is working with asynchronous method with firebase methods
I am trying to count initial li and after fetching data from server (using unit test case), but I am getting Cannot read property 'length' of null
My code:
import { ComponentFixture, TestBed, async, getTestBed } from '#angular/core/testing';
import { By } from '#angular/platform-browser';
import { HttpClientTestingModule, HttpTestingController } from '#angular/common/http/testing';
import { HttpClientModule, HttpClient } from '#angular/common/http';
import { MockAppService } from './mock.service'
import { DebugElement } from '#angular/core';
import { AppComponent } from './app.component';
import { AppService } from './app.service';
describe('AppComponent', () => {
let fixture: ComponentFixture<AppComponent>,
component: AppComponent,
service: AppService,
debugEl:DebugElement,
el:HTMLElement;
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [HttpClientTestingModule],
declarations: [
AppComponent
],
providers: [{ provide: AppService, useClass: MockAppService }]
}).compileComponents();
fixture = TestBed.createComponent(AppComponent);
component = fixture.componentInstance;
service = TestBed.get(AppService);
el = fixture.nativeElement;
spyOn(service, 'getData').and.callThrough();
}));
afterEach(() => {
//httpMock.verify();
});
describe('AppComponent onit test', () => {
it('intial list item', async(() => {
debugEl = fixture.debugElement.query(By.css('li'));
expect(fixture.nativeElement.querySelector('li').length()).toBe(0);
}));
it('should called appService getData method', async(() => {
fixture.detectChanges();
expect(service.getData).toHaveBeenCalled();
}));
it('should return an Observable<User[]>', () => {
const dummyUsers = [{
userId: 10,
id: 10,
title: "post",
body: "post"
}];
service.getData().subscribe(users => {
console.log(users)
expect(users.length).toBe(1);
expect(users).toEqual(dummyUsers);
expect(fixture.nativeElement.querySelector('li').length()).toBe(1);
});
});
})
});
code link
https://stackblitz.com/edit/angular-testing-w9towo?file=app%2Fapp.component.spec.ts
query does not return an array, try to use queryAll to return an array instead of query
here is the solution:
it('intial list item', async(() => {
let liCount= fixture.debugElement.queryAll(By.css('li'));
expect(liCount.length).toBe(0);
}));
When you look at this documentation: https://angular.io/api/core/DebugElement
You will find that DebugElement.query method returns DebugElement
and the method DebugElement.queryAll method returns DebugElement[]
so DebugElement.queryAll will return an array of DebugElement
and in this case, since it's an array you can then apply length property to it.
So you have to use DebugElement.queryAll in order to test the length of the resulting array.
I am trying to test component in angular .following thing I need to test
service function is called or not
how to mock the response
here is my code
https://stackblitz.com/edit/angular-testing-w9towo?file=app%2Fapp.component.spec.ts
spec.ts
import { ComponentFixture,TestBed, async,getTestBed } from '#angular/core/testing';
import { HttpClientTestingModule, HttpTestingController } from '#angular/common/http/testing';
import { HttpClientModule, HttpClient } from '#angular/common/http';
import { AppComponent } from './app.component';
import { AppService } from './app.service';
describe('AppComponent', () => {
let fixture:ComponentFixture<AppComponent>,
component:AppComponent,
injector:TestBed,
service:AppService,
httpMock:HttpTestingController,
el:HTMLElement;
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [HttpClientTestingModule],
declarations: [
AppComponent
],
providers: [ AppService ]
}).compileComponents();
}));
afterEach(() => {
//httpMock.verify();
});
fixture = TestBed.createComponent(AppComponent);
component = fixture.componentInstance;
// injector = getTestBed();
// service = injector.get(AppService);
// httpMock = injector.get(HttpTestingController);
spyOn('appService',getData);
describe('AppComponent onit test', () => {
it('should create the app', async(() => {
expect(true).toBe(true);
}));
it('should called appService getData method', async(() => {
expect(appService.getData).toHaveBeenCalled();
}));
})
});
getting error
cannot read property 'injector' of null
you can mock the service that way:
const mockPosts: Posts = {
userId: 10,
id: 10,
title: "post",
body: "post"};
class MockAppService extends AppService{
public getData() {
return Observable.of(mockPosts)
}
}
and use that mock class in your providers instead of the service
{ provide: AppService, useClass: MockAppService },
and add this:
fixture = TestBed.createComponent(AppComponent);
component = fixture.componentInstance;
appservice = TestBed.get(AppService); // this line
you can spyOn that service and return a value like this
spyOn(appservice , 'getData').and.returnValue("your value")
final file
import { ComponentFixture,TestBed, async,getTestBed } from '#angular/core/testing';
import { HttpClientTestingModule, HttpTestingController } from '#angular/common/http/testing';
import { HttpClientModule, HttpClient } from '#angular/common/http';
import { AppComponent } from './app.component';
import { AppService } from './app.service';
import { Observable } from 'rxjs/Observable';
import { Posts } from './post.interface';
import 'rxjs/add/observable/of';
const mockPosts: Posts =
{userId: 10,
id: 10,
title: "post",
body: "post",};
class MockAppService extends AppService {
public getData(){
return Observable.of(mockPosts)
}
}
describe('AppComponent', () => {
let fixture:ComponentFixture<AppComponent>,
component:AppComponent,
injector:TestBed,
service:AppService,
httpMock:HttpTestingController,
el:HTMLElement;
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [HttpClientTestingModule],
declarations: [
AppComponent
],
providers: [
{ provide: AppService, useClass: MockAppService }
]
}).compileComponents();
fixture = TestBed.createComponent(AppComponent);
component = fixture.componentInstance;
service = TestBed.get(AppService)
// injector = getTestBed();
// service = injector.get(AppService);
// httpMock = injector.get(HttpTestingController);
spyOn(service,'getData');
}));
afterEach(() => {
//httpMock.verify();
});
fixture = TestBed.createComponent(AppComponent);
component = fixture.componentInstance;
service = TestBed.get(AppService)
// injector = getTestBed();
// service = injector.get(AppService);
// httpMock = injector.get(HttpTestingController);
spyOn(service,'getData');
describe('AppComponent onit test', () => {
it('should create the app', async(() => {
expect(true).toBe(true);
}));
it('should called appService getData method', async(() => {
fixture.detectChanges();
expect(service.getData).toHaveBeenCalled();
}));
})
});
I'm trying to cover my service call in the component with test case as described below.
But getting error while running ng test as shown below:
Failed: this.monthService.getMonthView(...).then is not a function
TypeError: this.monthService.getMonthView(...).then is not a function
Service: (MonthService.ts)
getMonthView is the service call inside MonthService
getMonthView(forecastMonth: string): Promise<any[]> {
const options = new RequestOptions({ headers: this.headers });
const url = this.BaseUrl + forecastMonth;
return this.http.get(url, options)
.toPromise()
.then(response => response.json() as any[])
.catch(this.handleError);
}
Component: (MonthComponent.ts)
MonthService is imported and injected into MonthComponent.ts
loadDataOnMonthViewClicked(forecastMonth) {
this.monthService.getMonthView(forecastMonth)
.then(response =>
this.result = response
);
}
Test Suite :
beforeEach(() => {
/**
* Ref:https://angular.io/guide/testing#!#component-fixture
*/
fixture = TestBed.createComponent(MonthComponent);
component = fixture.componentInstance;
myService = TestBed.get(MonthService );
//fixture.detectChanges();
});
it('should be created', () => {
expect(component).toBeTruthy();
});
fit('should test loadDataOnMonthViewClick', async(() => {
let data=["10","20","30"];
spyOn(component.monthService, 'getMonthView').and.returnValue(result);
component.loadDataOnMonthViewClicked('Jan');
fixture.detectChanges();
expect(component.result).toBe(data);
}));
Here's what I did to make it work.
// the component
import { Component, OnInit } from "#angular/core";
import { MonthService } from "./month.service";
#Component({
selector: "app-root",
template: "",
})
export class AppComponent {
result = undefined;
constructor(private monthService: MonthService) { }
loadDataOnMonthViewClicked(forecastMonth) {
this.monthService
.getMonthView(forecastMonth)
.then(response => (this.result = response));
}
}
and the service
// the service
import { Injectable } from "#angular/core";
import { Http } from "#angular/http";
import "rxjs/add/operator/toPromise";
#Injectable()
export class MonthService {
constructor(private http: Http) { }
getMonthView(_forecastMonth: string) {
return this.http
.get("http://jsonplaceholder.typicode.com/posts/1")
.toPromise()
.then(response => response.json())
.catch(console.error);
}
}
and the test
import { TestBed, async, ComponentFixture } from "#angular/core/testing";
import { AppComponent } from "./app.component";
import { MonthService } from "./month.service";
import { HttpModule } from "#angular/http";
import { Observable } from "rxjs/Observable";
import "rxjs/add/observable/of";
describe("AppComponent", () => {
let fixture: ComponentFixture<AppComponent>;
let component: AppComponent;
let myService;
beforeEach(
async(() => {
TestBed.configureTestingModule({
providers: [MonthService],
declarations: [AppComponent],
imports: [HttpModule],
}).compileComponents();
}),
);
beforeEach(() => {
fixture = TestBed.createComponent(AppComponent);
component = fixture.componentInstance;
myService = fixture.debugElement.injector.get(MonthService);
fixture.detectChanges();
});
it(
"should test loadDataOnMonthViewClick",
async(() => {
let data = ["10", "20", "30"];
spyOn(myService, "getMonthView").and.callFake(() =>
Promise.resolve(data),
);
component.loadDataOnMonthViewClicked("Jan");
fixture.whenStable().then(() => {
expect(component.result).toBe(data);
});
}),
);
});
But I suggest to mock the service altogether, with something like
providers: [
{ provide: FetchDataService, useClass: FetchDataServiceMock }
],