Angular 6: Служба тестирования с ошибками запроса httpclient - PullRequest
0 голосов
/ 27 августа 2018

У меня есть служба курсов, которая получает свои данные с внутреннего сервера. Я пытаюсь написать спецификацию для сервиса и компонентов, которые запрашивают у него данные. У меня есть только запросы. У меня было много сбоев, и я ничего не нашел в гугл-ответах.

это интерфейсы

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();
  });
});

Ответы [ 2 ]

0 голосов
/ 27 августа 2018

Я попытался использовать реальные данные, вместо этого использовать фиктивные данные, настроив ожидаемые курсы следующим образом: Ожидаемые курсы = курс обслуживания.getCourses (); и selectedCourse, подобный этому: ожидается, курс = курс, сервис. и большая часть ошибки ушла, так что это то, что я сделал.

спасибо всем, кто мне ответил!

0 голосов
/ 27 августа 2018

В сервисной спецификации курса удалите 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[];
  });

Вы можете видеть, что опечатка в обозначении объекта была: сегменты = [, который должен быть сегментами: [. Пожалуйста, исправьте во всех местах

Здесь я тоже вижу опечатку:

  // Test getCoures()
  it('should return all courses', () => {
    courseService.getCourses().subscribe(
      //was courses => expect(courses).toEqual(expectedCourses]))
      //should be
      courses => expect(courses).toEqual(expectedCourses)
  )
  });

UPDATE

Ошибка Ошибка: Не найдено никаких открытых запросов, найдено 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);
});

Для следующего утверждения используйте тот же подход.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...