如何使用 jasmineSpy 测试 Angular 组件?
How to use jasmineSpy to test Angular component?
我有如下组件和服务。我想编写一个单元测试来测试这个组件,尝试使用 Spy 来模拟服务,但是它在启动组件时抛出 "no provider for HttpClient" 错误,因为我的 NewsService 依赖于 HttpClient。
我应该如何调整我的代码以注入 HttpClient?
主页组件:
import { Component,Inject } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { HttpHeaders } from '@angular/common/http';
import { Observable } from 'rxjs/Observable';
import { Router } from '@angular/router';
import { News } from '../models/news';
import { NewsService } from '../service/news.service';
@Component({
selector: 'app-home',
templateUrl: './home.component.html',
styleUrls: ['./home.component.css'],
providers: [NewsService]
})
export class HomeComponent {
public news: News[];
public headline: News;
private baseUrl: string;
constructor(private router: Router, private service: NewsService) {
}
ngOnInit() {
let baseUrl = document.getElementsByTagName('base')[0].href;
var headurl = this.baseUrl + "api/News/Headline?country=us&category=business";
this.service.getNews(headurl).subscribe((res) => { this.headline = res[0]; });
var url = this.baseUrl + "api/News/Category?country=us&category=business";
this.service.getNews(url).subscribe((res) => this.news = res);
}
新闻服务
import { Injectable } from '@angular/core';
import { HttpClient, HttpParams } from '@angular/common/http';
import { HttpHeaders } from '@angular/common/http';
import { News } from '../models/news';
import { Observable } from 'rxjs';
const httpOptions = {
headers: new HttpHeaders({
'Content-Type': 'application/json'
})
};
@Injectable()
export class NewsService {
constructor(
private http: HttpClient) {
}
getNews(url:string): Observable<News[]> {
return this.http.get<News[]>(url);
}
}
测试
import { async, fakeAsync, ComponentFixture, TestBed, tick } from '@angular/core/testing';
import { HttpClient, HttpResponse } from '@angular/common/http';
import { News } from '../models/news';
import { NewsService } from '../service/news.service';
import { HomeComponent } from '../home/home.component';
import { of } from 'rxjs/observable/of';import { Router } from '@angular/router';
describe('HomeComponent', () => {
let component: HomeComponent;
let fixture: ComponentFixture < HomeComponent > ;
let getNewsSpy: jasmine.Spy;
let headlineEl: HTMLElement;
let testheadline: News[];
let url: string;
beforeEach(() => {
testheadline = [{
NewsId: 'test',
author: 'test',
description: 'headline',
publishedAt: '2018-8-8',
title: 'Android ',
url: 'http://www.ghostchina.com',
urlToImage: 'http://static.ghostchina.com/image/c/06/765c76cb1ca259dd8fe8002459bbc.jpg'
}] as News[];
const newsService = jasmine.createSpyObj('NewsService', ['getNews']);
let url = 'api/News/Headline?country=us&category=business';
getNewsSpy = newsService.getNews.and.returnValue( of (testheadline));
const routerSpy = jasmine.createSpyObj('Router', ['navigate']);
TestBed.configureTestingModule({
declarations: [HomeComponent],
providers: [{
provide: NewsService,
useValue: newsService
}, {
provide: Router,
useValue: routerSpy
}]
});
fixture = TestBed.createComponent(HomeComponent);
component = fixture.componentInstance;
});
describe('#oninit', () => {
it('should return expected category news (called once)', () => {
let url = 'api/News/Category?country=us&category=business';
fixture.detectChanges(); // onInit()
expect(getNewsSpy.calls.any()).toBe(false, 'getNews not yet called'); }); });
您因此而收到错误。
@Component({
providers: [NewsService]
})
当我们使用 @component.providers
在 组件级别 中提供服务时。它优先于任何全局提供程序,这使得提供程序 仅适用于组件 .
所以没有必要为测试模块提供存根服务。(因为您有组件级服务。)
您要做的就是为组件提供服务存根。为此,您可以使用 TestBed.overrideComponent
方法。使用它我们可以覆盖组件的模板和提供者。
const newsService = jasmine.createSpyObj('NewsService', ['getNews']);
TestBed.configureTestingModule({
declarations: [HomeComponent]
});
TestBed.overrideComponent(HomeComponent, {
set: {
providers: [
{provide: NewsService,useValue: newsService}
]
}
})
您可以在 Angular Testing Docs
中阅读更多有关 覆盖组件的提供程序 的信息
我有如下组件和服务。我想编写一个单元测试来测试这个组件,尝试使用 Spy 来模拟服务,但是它在启动组件时抛出 "no provider for HttpClient" 错误,因为我的 NewsService 依赖于 HttpClient。
我应该如何调整我的代码以注入 HttpClient?
主页组件:
import { Component,Inject } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { HttpHeaders } from '@angular/common/http';
import { Observable } from 'rxjs/Observable';
import { Router } from '@angular/router';
import { News } from '../models/news';
import { NewsService } from '../service/news.service';
@Component({
selector: 'app-home',
templateUrl: './home.component.html',
styleUrls: ['./home.component.css'],
providers: [NewsService]
})
export class HomeComponent {
public news: News[];
public headline: News;
private baseUrl: string;
constructor(private router: Router, private service: NewsService) {
}
ngOnInit() {
let baseUrl = document.getElementsByTagName('base')[0].href;
var headurl = this.baseUrl + "api/News/Headline?country=us&category=business";
this.service.getNews(headurl).subscribe((res) => { this.headline = res[0]; });
var url = this.baseUrl + "api/News/Category?country=us&category=business";
this.service.getNews(url).subscribe((res) => this.news = res);
}
新闻服务
import { Injectable } from '@angular/core';
import { HttpClient, HttpParams } from '@angular/common/http';
import { HttpHeaders } from '@angular/common/http';
import { News } from '../models/news';
import { Observable } from 'rxjs';
const httpOptions = {
headers: new HttpHeaders({
'Content-Type': 'application/json'
})
};
@Injectable()
export class NewsService {
constructor(
private http: HttpClient) {
}
getNews(url:string): Observable<News[]> {
return this.http.get<News[]>(url);
}
}
测试
import { async, fakeAsync, ComponentFixture, TestBed, tick } from '@angular/core/testing';
import { HttpClient, HttpResponse } from '@angular/common/http';
import { News } from '../models/news';
import { NewsService } from '../service/news.service';
import { HomeComponent } from '../home/home.component';
import { of } from 'rxjs/observable/of';import { Router } from '@angular/router';
describe('HomeComponent', () => {
let component: HomeComponent;
let fixture: ComponentFixture < HomeComponent > ;
let getNewsSpy: jasmine.Spy;
let headlineEl: HTMLElement;
let testheadline: News[];
let url: string;
beforeEach(() => {
testheadline = [{
NewsId: 'test',
author: 'test',
description: 'headline',
publishedAt: '2018-8-8',
title: 'Android ',
url: 'http://www.ghostchina.com',
urlToImage: 'http://static.ghostchina.com/image/c/06/765c76cb1ca259dd8fe8002459bbc.jpg'
}] as News[];
const newsService = jasmine.createSpyObj('NewsService', ['getNews']);
let url = 'api/News/Headline?country=us&category=business';
getNewsSpy = newsService.getNews.and.returnValue( of (testheadline));
const routerSpy = jasmine.createSpyObj('Router', ['navigate']);
TestBed.configureTestingModule({
declarations: [HomeComponent],
providers: [{
provide: NewsService,
useValue: newsService
}, {
provide: Router,
useValue: routerSpy
}]
});
fixture = TestBed.createComponent(HomeComponent);
component = fixture.componentInstance;
});
describe('#oninit', () => {
it('should return expected category news (called once)', () => {
let url = 'api/News/Category?country=us&category=business';
fixture.detectChanges(); // onInit()
expect(getNewsSpy.calls.any()).toBe(false, 'getNews not yet called'); }); });
您因此而收到错误。
@Component({
providers: [NewsService]
})
当我们使用 @component.providers
在 组件级别 中提供服务时。它优先于任何全局提供程序,这使得提供程序 仅适用于组件 .
所以没有必要为测试模块提供存根服务。(因为您有组件级服务。)
您要做的就是为组件提供服务存根。为此,您可以使用 TestBed.overrideComponent
方法。使用它我们可以覆盖组件的模板和提供者。
const newsService = jasmine.createSpyObj('NewsService', ['getNews']);
TestBed.configureTestingModule({
declarations: [HomeComponent]
});
TestBed.overrideComponent(HomeComponent, {
set: {
providers: [
{provide: NewsService,useValue: newsService}
]
}
})
您可以在 Angular Testing Docs
中阅读更多有关 覆盖组件的提供程序 的信息