Angular
  • Angular learning
  • Angular
    • Change Detection
      • Angular Change Detection Strategies
      • Understanding Change Detection Strategy in Angular
    • Angular Components Overview
      • Lifecycle hooks
      • View encapsulation
    • Text interpolation
    • Pipes
    • ARIA
    • Event binding
    • Directives
    • Dependency injection in Angular
    • Difference between Template-Driven and Reactive Forms
    • Guards
    • Resolvers
      • Resolver example
  • Memory management in Angular applications
  • Renderer2
  • Angular test
    • Testing
      • The different types of tests
      • Some testing best practices
      • Angular Service Testing in Depth
        • About Jasmine
        • Jasmine - Test suites
        • Implementation of First Jasmine Specfication
        • spyOn() & jasmine.createSpyObj()
        • beforeEach()
        • Testing services
        • Disabled and Focused Tests
        • flush
        • HttpTestingController
        • Sample code
      • Angular Component Testing in Depth
        • Intro to Angular Component testing
        • DOM interaction
        • Trigger Change Detection
        • Test Card List Test Suite Conclusion
        • Window.requestAnimationFrame()
        • Asynchronous Work (Jasmine)
        • Cooperative asynchronous JavaScript: Timeouts and intervals
        • FakeAsync - Asynchronous Work (Jasmine) part 2
        • tick()
        • Sample codes
      • Testing Promised-based code-intro Microtasks
        • Microtasks
        • Micro-tasks within an event loop (Summary)
        • Macro-tasks within an event loop (Summary)
        • Test promised Microtasks (code)
      • Using fakeAsync to test Async Observables
      • Cypress.io
        • Create our first e2e test
      • Angular CLI code coverage and deployment in prod mode.
      • Travis CI
  • Angular best practices
    • Angular best practices
      • Security
      • Accessibility in Angular
      • Keeping your Angular projects up-to-date
    • Bootstrapping an Angular Application
      • Understanding the File Structure
      • Bootstrapping Providers
    • Components in Angular
      • Creating Components
      • Application Structure with Components
        • Accessing Child Components from Template
        • Using Two-Way Data Binding
        • Responding to Component Events
        • Passing Data into a Component
      • Projection
      • Structuring Applications with Components
      • Using Other Components
  • Reactive extensions
    • RxJS
      • RxJS Operators
      • of
      • Observable
      • async pipe (Angular)
      • Interval
      • fromEvent
      • Pipe
      • Map
      • Tap
      • ShareReplay
      • Concat
      • ConcatMap
      • Merge
      • MergeMap
      • ExhaustMap
      • fromEvent
      • DebounceTime
        • Type Ahead
      • Distinct Until Changed
      • SwitchMap
      • CatchError
      • Finalize
      • RetryWhen
      • DelayWhen
      • ForkJoin
      • First
      • Interview Questions
      • Zip
  • NgRx
    • What's NgRx
      • Actions
      • Reducers
      • Selectors
      • 🙅‍♂️Authentication guard with NgRX
      • @ngrx/effects
        • Side-Effect refresh survivor
  • Interview Q&A
    • Angular Unit Testing Interview Questions
    • Angular Questions And Answers
  • Angular Advanced
    • Setting up our environment
      • Understanding Accessors (TS)
      • The host & ::ng-deep Pseudo Selector
Powered by GitBook
On this page

Was this helpful?

  1. Angular test
  2. Testing
  3. Angular Service Testing in Depth

Sample code

First, we need to cover our service code:

import { Injectable } from '@angular/core';
import { HttpClient, HttpParams } from '@angular/common/http';
import { Observable } from 'rxjs';
import { Course } from '../model/course';
import { map } from 'rxjs/operators';
import { Lesson } from '../model/lesson';


@Injectable()
export class CoursesService {

    constructor(private http: HttpClient) {

    }

    findCourseById(courseId: number): Observable<Course> {
        return this.http.get<Course>(`/api/courses/${courseId}`);
    }

    findAllCourses(): Observable<Course[]> {
        return this.http.get('/api/courses')
            .pipe(
                map(res => res['payload'])
            );
    }
    
    saveCourse(courseId: number, changes: Partial<Course>): Observable<Course> {
        return this.http.put<Course>(`/api/courses/${courseId}`, changes);
    }

    findLessons(
        courseId: number, filter = '', sortOrder = 'asc',
        pageNumber = 0, pageSize = 3): Observable<Lesson[]> {

        return this.http.get('/api/lessons', {
            params: new HttpParams()
                .set('courseId', courseId.toString())
                .set('filter', filter)
                .set('sortOrder', sortOrder)
                .set('pageNumber', pageNumber.toString())
                .set('pageSize', pageSize.toString())
        }).pipe(
            map(res => res['payload'])
        );
    }
}

And now we will cover the spec file:

import { TestBed } from '@angular/core/testing';
import { CoursesService } from './courses.service';
import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
import { COURSES, findLessonsForCourse } from '../../../../server/db-data';
import { Course } from '../model/course';
import { HttpErrorResponse } from '@angular/common/http';

describe('CoursesService', () => {
    let coursesService: CoursesService,
        httpTestingController: HttpTestingController;

    beforeEach(() => {
        TestBed.configureTestingModule({
            imports: [HttpClientTestingModule],
            providers: [
                CoursesService
            ]
        });

        coursesService = TestBed.inject(CoursesService);
        httpTestingController = TestBed.inject(HttpTestingController);
    });

    it('Should retreive all courses', () => {
        coursesService.findAllCourses()
            .subscribe(courses => {
                expect(courses).toBeTruthy('No courses returned');

                expect(courses.length).toBe(12,
                    'Incorrect number of courses');

                const course = courses.find(course => course.id === 1);

                expect(course.titles.description).toBe(
                    'Serverless Angular with Firebase Course');
            });

        const req = httpTestingController.expectOne('/api/courses');
        expect(req.request.method).toEqual('GET');
        req.flush({ payload: Object.values(COURSES) });
    });

    it('Should find a course by id', () => {
        coursesService.findCourseById(12)
            .subscribe(course => {
                expect(course).toBeTruthy();
                expect(course.id).toBe(12);
            });

        const req = httpTestingController.expectOne('/api/courses/12');
        expect(req.request.method).toEqual('GET');
        req.flush(COURSES[12]);
    });

    it('should save the course data', () => {
        const changes: Partial<Course> = { titles: { description: 'Testing course' } };

        coursesService.saveCourse(12, changes).subscribe(course => {
            expect(course.id).toBe(12);
        });

        const req = httpTestingController.expectOne('/api/courses/12');

        expect(req.request.method).toEqual('PUT');

        expect(req.request.body.titles.description).toEqual(changes.titles.description);

        req.flush({
            ...COURSES[12],
            ...changes
        });
    });

    it('Should give an error if save course fails', () => {
        const changes: Partial<Course> = { titles: { description: 'Testing course' } };

        coursesService.saveCourse(12, changes)
            .subscribe(
                () => fail('The save course operation should have failed'),
                (error: HttpErrorResponse) => {
                    expect(error.status).toBe(500);
                });

        const req = httpTestingController.expectOne('/api/courses/12');
        expect(req.request.method).toEqual('PUT');
        req.flush('Save course failed',
            {
                status: 500,
                statusText: 'Internal server error'
            });
    });

    it('should find a list of lessosns', () => {
        coursesService.findLessons(12)
            .subscribe(lessons => {
                expect(lessons).toBeTruthy();

                expect(lessons.length).toBe(3);
            });

        // tslint:disable-next-line: no-shadowed-variable
        const req = httpTestingController.expectOne(req => req.url == '/api/lessons');

        // Expects
        expect(req.request.method).toBe('GET');
        expect(req.request.params.get('courseId')).toEqual('12');
        expect(req.request.params.get('filter')).toEqual('');
        expect(req.request.params.get('sortOrder')).toEqual('asc');
        expect(req.request.params.get('pageNumber')).toEqual('0');
        expect(req.request.params.get('pageSize')).toEqual('3');

        req.flush({
            payload: findLessonsForCourse(12).slice(0, 3)
        });
    });

    afterEach(() => {
        httpTestingController.verify();
    });
});

After get completed all the test methods we need to run our command ng test and this should be displayed in the screen:

PreviousHttpTestingControllerNextAngular Component Testing in Depth

Last updated 4 years ago

Was this helpful?