如何使用 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

中阅读更多有关 覆盖组件的提供程序 的信息