How to test that I have loaded my JSON - Angular2+ - javascript

I have an Angular method that simply loads the contents of a locally stored JSON file featuring an array, however I cannot seem to test it.
test.ts (shortened for conciseness)
describe('MyComponent', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [MyComponent],
imports: [HttpClientTestingModule],
});
fixture = TestBed.createComponent(MyComponent);
component = fixture.componentInstance;
component.ngOnInit();
it('should load status data from local json', () => {
const data = require('../../../assets/myData.json');
component.getThings();
expect(component.data).toEqual(data);
});
}
MyComponent.ts
data: string[];
constructor(private httpClient: HttpClient) {}
ngOnInit() {
this.getThings().subscribe(data =
this.data = data;
}
}
getData(): Observable<any> {
const data = '../../../assets/data.json';
return this.httpClient.get(data);
}

When testing for http request you need to mock the request.
Check out more about HttpClientTestingModule below:
https://angular.io/api/common/http/testing/HttpClientTestingModule
https://medium.com/spektrakel-blog/angular-testing-snippets-httpclient-d1dc2f035eb8
The below code is working,updated the component code as well a bit:
Component:
import { Component, OnInit } from '#angular/core';
import { HttpClient } from '#angular/common/http';
import { Observable } from 'rxjs';
#Component({
selector: 'app-load-local-file',
templateUrl: './load-local-file.component.html',
styleUrls: ['./load-local-file.component.css']
})
export class LoadLocalFileComponent implements OnInit {
data: string[];
constructor(private httpClient: HttpClient) { }
ngOnInit() {
this.getData().subscribe(data => {
this.data = data;
});
}
getData(): Observable<any> {
const data = './data.json';
return this.httpClient.get(data);
}
}
Spec:
import { TestBed, async, fakeAsync, tick } from '#angular/core/testing';
import { HttpClientTestingModule, HttpTestingController } from '#angular/common/http/testing'
import { LoadLocalFileComponent } from 'src/app/load-local-file/load-local-file.component';
describe('MyComponent', () => {
let fixture, component, httpMock: HttpTestingController;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [LoadLocalFileComponent],
imports: [HttpClientTestingModule]
});
fixture = TestBed.createComponent(LoadLocalFileComponent);
component = fixture.componentInstance;
httpMock = TestBed.get(HttpTestingController);
}));
it('should load status data from local json', fakeAsync(() => {
const data = require('./data.json');
component.ngOnInit();
tick();
const request = httpMock.expectOne('./data.json');
request.flush(data);
expect(component.data).toEqual(data);
}));
});

Related

Angular unit test case- Cannot read properties of undefined (reading 'forEachNode')

I am trying to write unit test cases for Grid, for this I need to loop through gridapi data but it is giving me Cannot read undefined forEachNode error.
myComponent.component.ts.
import { Component, OnInit, } from '#angular/core';
import { GridOptions, GridApi, } from "ag-grid-community";
#Component({
selector: 'my-component',
templateUrl: './mycomponent.component.html',
styleUrls: ['./mycomponent.component.scss']
})
export class MyComponent implements OnInit {
grid: GridOptions;
data;
constructor() {}
getData() {
this.grid.api.forEachNode((node) =>
this.data.push(node.data.id);
);
}
}
mycomponent.component.spec.ts:
import { ComponentFixture, TestBed, waitForAsync } from '#angular/core/testing';
import { Mycomponent} from './mycomponent.component';
describe('Mycomponent', () => {
let component: Mycomponent;
let fixture: ComponentFixture<Mycomponent>;
beforeEach(
waitForAsync(() => {
TestBed.configureTestingModule({
declarations: [Mycomponent],
}).compileComponents();
})
);
beforeEach(() => {
fixture = TestBed.createComponent(Mycomponent);
component = fixture.componentInstance;
const gridObj = jasmine.createSpyObj('grid', [
'api',
]);
gridObj.grid.api.and.returnValue({
context: {}
data : { id:1,name: 'columName'}
});
component.grid = gridObj;
fixture.detectChanges();
});
it(`should call getData method.`, () => {
component.getData();
expect(component.data).toEqual('[1]');
});
});
When I try to run above test case,
TypeError: Cannot read properties of undefined (reading 'forEachNode').
Can any one tell me where I am doing wrong.

Angular 10 Test: Component Resolver Data Subscription Error

I've spend the last few days trying to get up to speed with ng test and all the spec files #angular/cli creates when creating components and, well, pretty much else.
As I was working on my own portfolio website, I have come across an issue that I cannot seem to understand or fix.
I have this component (pretty vanilla stuff):
import { Component, OnInit } from '#angular/core';
import { ActivatedRoute } from '#angular/router';
import { Title } from '#angular/platform-browser'
import { ProjectDetails } from './project-details'
#Component({
selector: 'app-projects-details',
templateUrl: './projects-details.component.html',
styleUrls: ['./projects-details.component.sass']
})
export class ProjectsDetailsComponent implements OnInit {
// Class variables
currentContent: ProjectDetails
constructor(
private route : ActivatedRoute,
private title: Title
) { }
ngOnInit() {
// Assign the data to local variable for use
this.route.data.subscribe(content => {
this.currentContent = content.project.view //<-- This line causes the issue
// Set the title for the Projects view
this.title.setTitle(this.currentContent.view_title)
})
}
}
And this spec file (more vanilla stuff):
import { async, ComponentFixture, TestBed } from '#angular/core/testing';
import { RouterTestingModule } from '#angular/router/testing'
import { ProjectsDetailsComponent } from './projects-details.component';
import { ProjectDetails } from './project-details'
describe('ProjectsDetailsComponent', () => {
let component: ProjectsDetailsComponent;
let fixture: ComponentFixture<ProjectsDetailsComponent>;
const projectDetails : ProjectDetails = { /* valid object content */ }
beforeEach(async(() => {
TestBed.configureTestingModule({
imports:[
RouterTestingModule
],
declarations: [ ProjectsDetailsComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(ProjectsDetailsComponent);
component = fixture.componentInstance;
component.currentContent = projectDetails
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
When running the tests, I get this error:
TypeError: content.project is undefined in http://localhost:9876/_karma_webpack_/main.js (line 1576)
So, I'm not sure exactly what's going on here. No matter what I do, the error prevails.I have a similarly setup component that doesn't have this issue and a side by side comparison shows no differences in the spec.ts file aside from imports.
I tried changing the file to this:
import { async, ComponentFixture, TestBed } from '#angular/core/testing';
import { RouterTestingModule } from '#angular/router/testing'
import { ProjectsDetailsComponent } from './projects-details.component';
import { ProjectDetails } from './project-details'
import { ActivatedRoute } from '#angular/router';
describe('ProjectsDetailsComponent', () => {
let component: ProjectsDetailsComponent;
let fixture: ComponentFixture<ProjectsDetailsComponent>;
const projectDetails : ProjectDetails = {/* valid content */}
beforeEach(async(() => {
TestBed.configureTestingModule({
// imports:[
// RouterTestingModule
// ],
providers: [
{ provide: ActivatedRoute, useValue: projectDetails }
],
declarations: [ ProjectsDetailsComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(ProjectsDetailsComponent);
component = fixture.componentInstance;
component.currentContent = projectDetails
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
Which changes the error to this (which confuses me more):
TypeError: this.route.data is undefined in http://localhost:9876/_karma_webpack_/main.js (line 1575)
The question to the community: how do I fix this? What's the reason this error is coming up?
Instead of providing the raw projectDetails, provide an Observable in its data property:
import {of} from 'rxjs';
...
beforeEach(async(() => {
TestBed.configureTestingModule({
providers: [
// Properly provide the activated route mock object.
{ provide: ActivatedRoute, useValue: { data: of(projectDetails) } }
],
declarations: [ ProjectsDetailsComponent ]
})
.compileComponents();
}));
...
If you look at how you access the route data, you can see that it uses an Observable:
this.route.data.subscribe(content => {...});

Angular fetching apis

My apis is http://dradiobeats.x10host.com/public/api/areas
my app.component.ts:
import { Component, OnInit, OnDestroy } from '#angular/core';
import { HttpClient } from '#angular/common/http';
import { map } from 'rxjs/operators';
import { Subscription } from 'rxjs';
import { Post } from './post.model';
import { PostsService } from './posts.service';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit, OnDestroy {
loadedPosts: Post[] = [];
isFetching = false;
error = null;
private errorSub: Subscription;
constructor(private http: HttpClient, private postsService: PostsService) {}
ngOnInit() {
this.errorSub = this.postsService.error.subscribe(errorMessage => {
this.error = errorMessage;
});
this.isFetching = true;
this.postsService.fetchPosts().subscribe(
posts => {
this.isFetching = false;
this.loadedPosts = posts;
},
error => {
this.isFetching = false;
this.error = error.message;
}
);
}
onCreatePost(postData: Post) {
// Send Http request
this.postsService.createAndStorePost( postData.id, postData.name, postData.domain, postData.description , postData.picture);
}
onFetchPosts() {
// Send Http request
this.isFetching = true;
this.postsService.fetchPosts().subscribe(
posts => {
this.isFetching = false;
this.loadedPosts = posts;
},
error => {
this.isFetching = false;
this.error = error.message;
console.log(error);
}
);
}
onClearPosts() {
// Send Http request
this.postsService.deletePosts().subscribe(() => {
this.loadedPosts = [];
});
}
onHandleError() {
this.error = null;
}
ngOnDestroy() {
this.errorSub.unsubscribe();
}
}
my posts.service.ts
import { Injectable } from '#angular/core';
import {
HttpClient,
HttpHeaders,
HttpParams,
HttpEventType
} from '#angular/common/http';
import { map, catchError, tap } from 'rxjs/operators';
import { Subject, throwError } from 'rxjs';
import { Post } from './post.model';
#Injectable({ providedIn: 'root' })
export class PostsService {
error = new Subject<string>();
constructor(private http: HttpClient) {}
createAndStorePost( id: string,
name: string,
description: string,
domain: string,
picture: any ) {
const postData: Post = { id , domain , name, description, picture };
this.http
.post<{ name: string , description: string , domain: string , picture: string }>(
'http://dradiobeats.x10host.com/api/areas',
postData,
{
observe: 'response'
}
)
.subscribe(
responseData => {
console.log(responseData);
},
error => {
this.error.next(error.message);
}
);
}
fetchPosts() {
let searchParams = new HttpParams();
searchParams = searchParams.append('print', 'pretty');
searchParams = searchParams.append('custom', 'key');
return this.http
.get<{ [key: string]: Post}>(
'http://dradiobeats.x10host.com/api/areas',
{
headers: new HttpHeaders({ 'Custom-Header': 'Hello' }),
params: searchParams,
responseType: 'json'
}
)
.pipe(
map(responseData => {
const postsArray: Post[] = [];
for (const key in responseData) {
if (responseData.hasOwnProperty(key)) {
postsArray.push({ ...responseData[key], id: key });
}
}
return postsArray;
}),
catchError(errorRes => {
// Send to analytics server
return throwError(errorRes);
})
);
}
deletePosts() {
return this.http
.delete('http://dradiobeats.x10host.com/api/areas', {
observe: 'events',
responseType: 'text'
})
.pipe(
tap(event => {
console.log(event);
if (event.type === HttpEventType.Sent) {
// ...
}
if (event.type === HttpEventType.Response) {
console.log(event.body);
}
})
);
}
}
my posts.model.ts
export interface Post {
id: string;
name: string;
domain: string;
description: string;
picture: any;
}
my logging-interceptor.service.ts
import {
HttpInterceptor,
HttpRequest,
HttpHandler,
HttpEventType
} from '#angular/common/http';
import { tap } from 'rxjs/operators';
export class LoggingInterceptorService implements HttpInterceptor {
intercept(req: HttpRequest<any>, next: HttpHandler) {
console.log('Outgoing request');
console.log(req.url);
console.log(req.headers);
return next.handle(req).pipe(
tap(event => {
if (event.type === HttpEventType.Response) {
console.log('Incoming response');
console.log(event.body);
}
})
);
}
}
my auth-interceptor.service.ts
import {
HttpInterceptor,
HttpRequest,
HttpHandler
} from '#angular/common/http';
export class AuthInterceptorService implements HttpInterceptor {
intercept(req: HttpRequest<any>, next: HttpHandler) {
const modifiedRequest = req.clone({
headers: req.headers.append('Auth', 'xyz')
});
return next.handle(modifiedRequest);
}
}
my app-routing.module.ts
import { NgModule } from '#angular/core';
import { Routes, RouterModule } from '#angular/router';
const routes: Routes = [];
#NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
my app.module.ts
import { BrowserModule } from '#angular/platform-browser';
import { NgModule } from '#angular/core';
import { FormsModule } from '#angular/forms';
import { HttpClientModule, HTTP_INTERCEPTORS } from '#angular/common/http';
import { AppComponent } from './app.component';
import { AuthInterceptorService } from './auth-interceptor.service';
import { LoggingInterceptorService } from './logging-interceptor.service';
#NgModule({
declarations: [AppComponent],
imports: [BrowserModule, FormsModule, HttpClientModule],
providers: [
{
provide: HTTP_INTERCEPTORS,
useClass: LoggingInterceptorService,
multi: true
},
{
provide: HTTP_INTERCEPTORS,
useClass: AuthInterceptorService,
multi: true
}
],
bootstrap: [AppComponent]
})
export class AppModule {}
my app.component.spec.ts
import { TestBed, async } from '#angular/core/testing';
import { AppComponent } from './app.component';
describe('AppComponent', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [
AppComponent
],
}).compileComponents();
}));
it('should create the app', () => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.debugElement.componentInstance;
expect(app).toBeTruthy();
});
it(`should have as title 'ng-complete-guide-update'`, () => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.debugElement.componentInstance;
expect(app.title).toEqual('ng-complete-guide-update');
});
it('should render title in a h1 tag', () => {
const fixture = TestBed.createComponent(AppComponent);
fixture.detectChanges();
const compiled = fixture.debugElement.nativeElement;
expect(compiled.querySelector('h1').textContent).toContain('Welcome to ng-complete-guide-update!');
});
});
my app.component.html
import { TestBed, async } from '#angular/core/testing';
import { AppComponent } from './app.component';
describe('AppComponent', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [
AppComponent
],
}).compileComponents();
}));
it('should create the app', () => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.debugElement.componentInstance;
expect(app).toBeTruthy();
});
it(`should have as title 'ng-complete-guide-update'`, () => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.debugElement.componentInstance;
expect(app.title).toEqual('ng-complete-guide-update');
});
it('should render title in a h1 tag', () => {
const fixture = TestBed.createComponent(AppComponent);
fixture.detectChanges();
const compiled = fixture.debugElement.nativeElement;
expect(compiled.querySelector('h1').textContent).toContain('Welcome to ng-complete-guide-update!');
});
});
my app.component.css
.container {
margin-top: 30px;
}
.row {
margin: 20px 0;
}
I am only getting data not elements [id ,name,description,domain,picture]why??

How to return data from service in angular4

Coming from the Angular1x background.I am migrating my existing app to Angular4
This is how my ng4 Service looks like
import { Injectable } from '#angular/core';
import {Http} from '#angular/http';
#Injectable()
export class DataService {
private _http : Http;
constructor(http:Http) {
this._http = http;
}
public GetPosts() : any{
this._http.get("https://jsonplaceholder.typicode.com/posts").subscribe(data => {
const posts = data.json();
console.log(posts); // logs the desired json
return posts;
})}}
Consuming the above service from the component.
import { Component, OnInit } from '#angular/core';
import {Customer} from './customer.model';
import {DataService } from '../../providers/data.service';
#Component({
selector: 'app-customer',
templateUrl: './customer.component.html',
styleUrls: ['./customer.component.css']
})
export class CustomerComponent implements OnInit {
private _dService: DataService;
constructor(dService:DataService) {
this._dService = dService;}
ngOnInit() {}
public GetAll(){
let posts =this._dService.GetPosts();
debugger;
console.log(posts); // undefined
/* here the posts is getting UNDEFINED (error) */
}}
In Angular1X, I used to return promise from ngService but how do the same in angular4??
You should subscribe to the observable in the component, not in the service.
In your service
public GetPosts() : any{
return this._http.get("https://jsonplaceholder.typicode.com/posts");
}
And in your component
this._dService.GetPosts().subscribe(data => {
const posts = data.json();
console.log(posts);
// Do whatever you like with posts
)};
The call to subscribe should be in the component's code
constructor(private http:Http) { }
getPosts(){
return this.http.get('https://jsonplaceholder.typicode.com/posts').map(
(response: Response) => {
return response.json();
}
)
}
And in the component:
the best practice you declare :
data : any;
this._dService.GetPosts().subscribe(
data => {this.data = data;
console.log(this.books);},
err => {console.log(err);},
()=> {console.log("terminated");}
);

Can't run service in Angular2 test (code copy pasted from the docs)

I have the following error (unit testing Angular2):
Cannot configure the test module when the test module has already been
instantiated. Make sure you are not using inject before
TestBed.configureTestingModule
Here is my code (it's basically a copy paste from the angular docs) which throws the above error:
import { TestBed, async } from '#angular/core/testing';
import { By } from '#angular/platform-browser';
import { AppModule } from './app.module';
import { AppComponent } from './app.component'
import { MyServiceService } from './my-service.service'
beforeEach(() => {
// stub UserService for test purposes
let userServiceStub = {
isLoggedIn: true,
user: { name: 'Test User'}
};
TestBed.configureTestingModule({
declarations: [ AppComponent],
providers: [ {provide: MyServiceService, useValue: userServiceStub } ]
});
let fixture = TestBed.createComponent(AppComponent);
let comp = fixture.componentInstance;
// UserService from the root injector
let userService = TestBed.get(MyServiceService);
// get the "welcome" element by CSS selector (e.g., by class name)
let de = fixture.debugElement.query(By.css('.nesto'));
let el = de.nativeElement;
it('should welcome "Bubba"', () => {
userService.user.name = 'something'; // welcome message hasn't been shown yet
fixture.detectChanges();
expect(el.textContent).toContain('some');
});
});
I want to run a service but it seems that I just can't do that.
The most likely problem is that you're attempting to run testing within your beforeEach(). You need to make sure all it() methods are outside/after the beforeEach():
beforeEach(() => {
// stub UserService for test purposes
let userServiceStub = {
isLoggedIn: true,
user: { name: 'Test User'}
};
TestBed.configureTestingModule({
declarations: [ AppComponent],
providers: [ {provide: MyServiceService, useValue: userServiceStub } ]
});
let fixture = TestBed.createComponent(AppComponent);
let comp = fixture.componentInstance;
// get the "welcome" element by CSS selector (e.g., by class name)
let de = fixture.debugElement.query(By.css('.nesto'));
let el = de.nativeElement;
});
it('should welcome "Bubba"', inject([MyServiceService], (userService) => {
userService.user.name = 'something'; // welcome message hasn't been shown yet
fixture.detectChanges();
expect(el.textContent).toContain('some');
}));
This works, removed the instance in the beforeEach() and injected into the it().
All credits go to Z. Bagley
import { TestBed, async, inject } from '#angular/core/testing';
import { By } from '#angular/platform-browser';
import { AppModule } from './app.module';
import { AppComponent } from './app.component'
import { MyServiceService } from './my-service.service'
import { Inject } from '#angular/core';
beforeEach(() => {
// stub UserService for test purposes
let userServiceStub = {
isLoggedIn: true,
user: { name: 'Test User'}
};
TestBed.configureTestingModule({
declarations: [ AppComponent],
providers: [ {provide: MyServiceService, useValue: userServiceStub } ]
});
});
it('should welcome "Bubba"', inject([MyServiceService], (userService) => {
let fixture=TestBed.createComponent(AppComponent);
fixture.detectChanges();
expect(userService.user.name).toContain('se');
}));

Categories

Resources