我有一个课程服务,可以从后端服务器获取其数据。我正在尝试为服务和向其请求数据的组件编写规范。我只有请求。我失败了很多,但在Google答案中什么都没找到。
这些是界面
export interface ICourse {
id: number;
title: string;
author: string;
segments: ISegment[];
}
export interface ISegment {
id: number;
unit_id: number;
unit_title: string;
name: string;
type: string;
data: string;
questions: IQuestion[];
}
export interface IQuestion {
id: number;
question: string;
answer1: string;
answer2: string;
answer3: string;
answer4: string;
correct: number;
}
课程服务错误:
CourseService getCourses should return all courses
Error: Expected no open requests, found 1: GET http://localhost:3000/courses.json
TypeError: Cannot read property 'getCourses' of undefined
TypeError: Cannot read property 'verify' of undefined
课程列表组件错误:
getCourses should return an Observable<ICourse[]>
Expected undefined to be truthy.
should create
[object ErrorEvent] thrown
这是组件和规格:
// ------ course.service ------ //
import { Injectable } from '@angular/core';
import { HttpClient, HttpErrorResponse, HttpHeaders } from '@angular/common/http';
import { BehaviorSubject } from 'rxjs';
import { Observable, throwError } from 'rxjs';
import { catchError, groupBy } from 'rxjs/operators';
import { ICourse } from './course';
import { HttpErrorHandler, HandleError } from './http-error-handler.service'
// Inject Data from Rails app to Angular app
@Injectable()
export class CourseService {
// JSON url to get data from
private url = 'http://localhost:3000/courses';
private courseUrl = 'http://localhost:3000/courses.json';
constructor(
private http: HttpClient) { }
// // Handle Any Kind of Errors
private handleError(error: HttpErrorResponse) {
// A client-side or network error occured. Handle it accordingly.
if (error.error instanceof ErrorEvent) {
console.error('An error occured:', error.error.message);
}
// The backend returned an unsuccessful response code.
// The response body may contain clues as to what went wrong.
else {
console.error(
'Backend returned code ${error.status}, ' +
'body was ${error.error}');
}
// return an Observable with a user-facing error error message
return throwError(
'Something bad happend; please try again later.');
}
// Get All Courses from Rails API App
getCourses(): Observable<ICourse[]> {
const coursesUrl = `${this.url}` + '.json';
return this.http.get<ICourse[]>(coursesUrl)
.pipe(catchError(this.handleError));
}
// Get Single Course by id. will 404 if id not found
getCourse(id: number): Observable<ICourse> {
const detailUrl = `${this.url}/${id}` + '.json';
return this.http.get<ICourse>(detailUrl)
.pipe(catchError(this.handleError));
}
}
// ------ course.service.spec ------ //
import { HttpClient, HttpResponse } from '@angular/common/http';
import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
import { TestBed, async, getTestBed, ComponentFixture, inject, tick, fakeAsync } from '@angular/core/testing';
import { DebugElemet } from '@angular/core';
import { By } from '@angular/platform-browser';
import { CourseService } from './course.service';
import { ICourse } from './course';
describe('CourseService', () => {
let httpClient: HttpClient;
let httpTestingController: HttpTestingController;
let courseService: CourseService;
// before each test, default value and delete old test
beforeEach(() => {
TestBed.configureTestingModule({
imports: [HttpClientTestingModule],
providers: [CourseService]
});
// Inject the http, test controller, and service-under-test
// as they will be referenced by each test.
httpClient = TestBed.get(HttpClient);
httpTestingController = TestBed.get(HttpTestingController);
courseService = TestBed.get(CourseService);
});
// After every test, assert that there are no more pending requests.
afterEach(() => {
httpTestingController.verify();
});
// ----------- CourseService method tests begin ----------- //
// Test getCourses()
describe('getCourses', () => {
// Mock Data to test the service
let expectedCourses: ICourse[];
beforeEach(() => {
courseService = TestBed.get(CourseService);
//expectedCourses = courseService.getCourses();
expectedCourses = [
{ id: 1, title: "Angular is Fun", author: "Son Goku", segments: [
{ id: 1, unit_id: 1, unit_title: "Components", name: "Lesson 1: Create Components", type: "Video", data: "www.hellokitty.com/angular1.flv" },
{ id: 2, unit_id: 1, unit_title: "Components", name: "Lesson 2: Decorators", type: "Video", data: "www.hellokitty.com/angular2.flv" },
{ id: 3, unit_id: 1, unit_title: "Components", name: "Lesson 3: Life Cycle", type: "Video", data: "www.hellokitty.com/angular3.flv" } ]
},
{ id: 2, title: "Ruby on Rails", author: "Monokuma", segments: [
{ id: 4, unit_id: 1, unit_title: "Introduction", name: "Lesson 1: Rails Console", type: "Video", data: "www.sdr2.com/rails1.flv" },
{ id: 5, unit_id: 2, unit_title: "Introduction", name: "Lesson 1: Gems", type: "Video", data: "www.sdr2.com/rails2.flv" } ]
},
{ id: 3, title: "Java", author: "Hououin Kyouma", segments: [
{ id: 6, unit_id: 1, unit_title: "Data Structures", name: "Lesson 1: Node", type: "Video", data: "www.deathnote.com/java1.flv" },
{ id: 7, unit_id: 1, unit_title: "Data Structures", name: "Lesson 2: Stack", type: "Video", data: "www.deathnote.com/java2.flv" },
{ id: 8, unit_id: 1, unit_title: "Data Structures", name: "Lesson 3: List", type: "Video", data: "www.deathnote.com/java3.flv" }]
}
] as ICourse[];
});
// Test getCoures()
it('should return all courses', () => {
courseService.getCourses().subscribe(
courses => expect(courses).toEqual(expectedCourses))
});
});
// Test getCourse(id)
describe('getCourse', () => {
// Mock Data to test the service
let expectedCourse: ICourse;
beforeEach(() => {
courseService = TestBed.get(CourseService);
expectedCourse = { id: 3, title: "Java", author: "Hououin Kyouma", segments = [
{ id: 6, unit_id: 1, unit_title: "Data Structures", name: "Lesson 1: Node", type: "Video", data: "www.deathnote.com/java1.flv" },
{ id: 7, unit_id: 1, unit_title: "Data Structures", name: "Lesson 2: Stack", type: "Video", data: "www.deathnote.com/java2.flv" },
{ id: 8, unit_id: 1, unit_title: "Data Structures", name: "Lesson 3: List", type: "Video", data: "www.deathnote.com/java3.flv" }]
} as ICourse;
});
it('should return course by id', () => {
courseService.getCourse(3).subscribe(
courses => expect(course).toEqual(expectedCourses[2]))
});
});
});
// ----------- course-list.component ----------- //
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute, Router, Routes } from '@angular/router';
import { ICourse } from '../course';
import { CourseService } from '../course.service';
// Course-list decorator
@Component({
selector: 'lg-course-list',
templateUrl: './course-list.component.html',
styleUrls: ['./course-list.component.sass']
})
export class CourseListComponent implements OnInit {
pageTitle = "Labguru Academy";
courses: ICourse[] =[];
errorMessage: string;
constructor(private courseService: CourseService,
private route: ActivatedRoute,
private router: Router) {
}
// Get list of courses from service
getCourseList() {
this.courseService.getCourses()
.subscribe(
courses => this.courses = courses,
errorMessage => this.errorMessage = <any>Error
);
}
// On start of the life cycle
ngOnInit() {
this.getCourseList();
}
}
// ----------- course-list.component.spec ----------- //
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';
import { HttpClientTestingModule } from '@angular/common/http/testing';
import { DebugElement } from '@angular/core';
import { By } from '@angular/platform-browser';
import { CourseService } from '../course.service';
import { CourseListComponent } from './course-list.component';
import { ICourse } from './course';
describe('CourseListComponent', () => {
let component: CourseListComponent;
let service: CourseService;
let fixture: ComponentFixture<CourseListComponent>;
let de: DebugElement;
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [ RouterTestingModule, HttpClientTestingModule ],
declarations: [ CourseListComponent ],
providers: [ CourseService ]
})
.compileComponents();
}));
beforeEach(() => {
service = new CourseService();
component = new CourseListComponent(service);
});
// Check the title of the course-list page
it(`should have as title 'Labguru Academy'`, async(() => {
fixture = TestBed.createComponent(CourseListComponent);
component = fixture.debugElement.componentInstance;
expect(component.pageTitle).toContain('Labguru Academy');
}));
// Test getCourses()
describe('getCourses', () => {
it('should return an Observable<ICourse[]>', () => {
fixture = TestBed.createComponent(CourseListComponent);
component = fixture.debugElement.componentInstance;
expect(component.getCourseList()).toBeTruthy();
});
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
答案 0 :(得分:3)
当然,服务规范会从导入数组中删除HttpTestingController,因为它不是模块:
TestBed.configureTestingModule({
imports: [HttpClientTestingModule],
providers: [CourseService]
});
以及尝试使用正确的对象符号:
expectedCourses = [
{ id: 1, title: "Angular is Fun", author: "Son Goku", segments: [
{ id: 1, unit_id: 1, unit_title: "Components", name: "Lesson 1: Create Components", type: "Video", data: "www.hellokitty.com/angular1.flv" },
{ id: 2, unit_id: 1, unit_title: "Components", name: "Lesson 2: Decorators", type: "Video", data: "www.hellokitty.com/angular2.flv" },
{ id: 3, unit_id: 1, unit_title: "Components", name: "Lesson 3: Life Cycle", type: "Video", data: "www.hellokitty.com/angular3.flv" } ]
},
{ id: 2, title: "Ruby on Rails", author: "Monokuma", segments: [
{ id: 4, unit_id: 1, unit_title: "Introduction", name: "Lesson 1: Rails Console", type: "Video", data: "www.sdr2.com/rails1.flv" },
{ id: 5, unit_id: 2, unit_title: "Introduction", name: "Lesson 1: Gems", type: "Video", data: "www.sdr2.com/rails2.flv" } ]
},
{ id: 3, title: "Java", author: "Hououin Kyouma", segments: [
{ id: 6, unit_id: 1, unit_title: "Data Structures", name: "Lesson 1: Node", type: "Video", data: "www.deathnote.com/java1.flv" },
{ id: 7, unit_id: 1, unit_title: "Data Structures", name: "Lesson 2: Stack", type: "Video", data: "www.deathnote.com/java2.flv" },
{ id: 8, unit_id: 1, unit_title: "Data Structures", name: "Lesson 3: List", type: "Video", data: "www.deathnote.com/java3.flv" }]
}
] as ICourse[];
});
您可以看到,这是对象符号中的错别字,是:segments = [,应该是segments:[。请在所有地方修复
在这里,我也看到错字:
// Test getCoures()
it('should return all courses', () => {
courseService.getCourses().subscribe(
//was courses => expect(courses).toEqual(expectedCourses]))
//should be
courses => expect(courses).toEqual(expectedCourses)
)
});
更新
错误错误:预期没有打开的请求,发现1:出现GET localhost:3000 / courses.json,因为您希望在afterEach块中请求:
// After every test, assert that there are no more pending requests.
afterEach(() => {
httpTestingController.verify();
});
要解决该问题,您可以使用httpTestingController模拟请求调用:https://angular.io/api/common/http/testing/HttpTestingController
我可以为您提供第一个帮助,而其他一些则需要您自己做: 当然是服务规格:
it('should return all courses', (done) => {
courseService.getCourses().subscribe(
courses => {
expect(courses).toEqual(expectedCourses));
done();
}
const request = httpTestingController.expectOne('/courses.json');
request.flush(expectedCourses);
});
对于以下断言,请使用相同的方法。
答案 1 :(得分:1)
我试图通过像这样配置ExpectedCourses来使用真实数据而不是模拟数据:ExpectedCourses = courseService.getCourses();和ExpectedCourse一样:ExpectedCourse = courseService.getCourse(3);并且大多数错误都消失了,所以这就是我所做的。
感谢所有回答我的人!