---
title: Bitovi Academy - Writing Unit Tests
description: Write a unit test for a service in Angular.
---

- ![AI implementation](https://www.bitovi.com/hubfs/AIConsultingIcon.svg)
  
  [AI implementation](https://www.bitovi.com/services/ai-consulting?hsLang=en-us)
- ![Systems engineering](https://www.bitovi.com/hubfs/icon%20-%20backend.svg)
  
  [Systems engineering](https://www.bitovi.com/services/systems-engineering-consulting?hsLang=en-us)
- ![Project management](https://www.bitovi.com/hubfs/icon%20-%20PM.svg)
  
  [Project management](https://www.bitovi.com/services/agile-project-management-consulting?hsLang=en-us)
- ![Product design](https://www.bitovi.com/hubfs/icon%20-%20design.svg)
  
  [Product design](https://www.bitovi.com/services/product-design-consulting?hsLang=en-us)
- ![Frontend development](https://www.bitovi.com/hubfs/icon%20-%20frontend.svg)
  
  [Frontend development](https://www.bitovi.com/services/frontend-development-consulting?hsLang=en-us)
- [View more
  
  →
  
  ](https://www.bitovi.com/digital-consulting-services?hsLang=en-us)

We're Experts in...

- [JavaScript](https://www.bitovi.com/services/frontend/javascript-consulting?hsLang=en-us)
- [AI training](https://www.bitovi.com/ai-training-for-software-engineers?hsLang=en-us)
- [Angular](https://www.bitovi.com/services/frontend/angular-consulting?hsLang=en-us)
- [Design Systems](https://www.bitovi.com/services/axure-figma-migration?hsLang=en-us)
- [React](https://www.bitovi.com/services/frontend/react-consulting?hsLang=en-us)
- [Temporal](https://www.bitovi.com/services/backend/temporal-consulting?hsLang=en-us)
- [React Native](https://www.bitovi.com/services/frontend/react-consulting/react-native?hsLang=en-us)
- [Node.js](https://www.bitovi.com/services/backend/nodejs-consulting-1?hsLang=en-us)

Showcase

![Yum! Brands](https://www.bitovi.com/hubfs/yum-showcase-link-1.png)

[View case study](https://www.bitovi.com/en/bitovi-yum-case-study?hsLang=en-us)

More Projects

- [![Levi's](https://www.bitovi.com/hubfs/levis.svg)](https://www.bitovi.com/web-application-consulting-work/levis-ecommerce-responsive-redesign?hsLang=en-us)
- [![Christie's International Real Estate](https://www.bitovi.com/hubfs/christies.svg)](https://design.bitovi.com/christies)
- [![BAFS](https://www.bitovi.com/hubfs/bafs.svg)](https://www.bitovi.com/ux-design-consulting/ux-case-studies/bafs-ppp?hsLang=en-us)
- [View more
  
  →
  
  ](https://www.bitovi.com/our-software-consulting-work?hsLang=en-us)

Open Source Tools

We build powerful tools and open source them to support the community.

[See what we've built →](https://www.bitovi.com/open-source?hsLang=en-us)

- [![Blog](https://www.bitovi.com/hubfs/icon%20-%20blog.svg)
  
  BlogWe post about perfecting software delivery
  
  ](https://bitovi.com/blog)
- [![Partnerships](https://www.bitovi.com/hubfs/Handshake-1.svg)
  
  PartnershipsLearn about Bitovi's technology partners
  
  ](https://www.bitovi.com/partnerships?hsLang=en-us)
- [![Academy](https://www.bitovi.com/hubfs/limbo/icons/icon-academy.svg)
  
  AcademyFree courses to build delivery skills
  
  ](https://www.bitovi.com/academy/?hsLang=en-us)
- [![Open source tools](https://www.bitovi.com/hubfs/icon%20-%20open%20source.svg)
  
  Open source toolsUse our tools or contribute to our open source mission.
  
  ](https://www.bitovi.com/open-source?hsLang=en-us)

Let's Connect

- [![Discord](https://www.bitovi.com/hubfs/DiscordLogo.svg)
  
  Discord
  
  ](https://discord.gg/J7ejFsZnJ4)
- [![LinkedIn](https://www.bitovi.com/hubfs/LinkedinLogo.svg)
  
  LinkedIn
  
  ](https://www.linkedin.com/company/bitovi/)
- [![GitHub](https://www.bitovi.com/hubfs/GithubLogo.svg)
  
  GitHub
  
  ](https://github.com/bitovi/)

![Eggbot](https://www.bitovi.com/hubfs/build_assets/bitovi-limbo-cms-react/338/js_client_assets/assets/eggbot-LTGhdSGL.png)

Name *

Work Email *

Phone

What's your project?

Send

### Contact Us

(312) 620-0386contact@bitovi.com

- [ Advanced JavaScript ](https://www.bitovi.com/academy/learn-advanced-javascript.html?hsLang=en-us)
- [ DOM and jQuery ](https://www.bitovi.com/academy/learn-dom-jquery.html?hsLang=en-us)
- [ Debug JavaScript ](https://www.bitovi.com/academy/learn-to-debug-javascript.html?hsLang=en-us)
- [ Learn TypeScript ](https://www.bitovi.com/academy/learn-typescript.html?hsLang=en-us)
- [ Learn React Native ](https://www.bitovi.com/academy/learn-react-native.html?hsLang=en-us)
- [ Learn Web Components ](https://www.bitovi.com/academy/learn-web-components.html?hsLang=en-us)
- [ Agile Program Management With Jira ](https://www.bitovi.com/academy/learn-agile-program-management-with-jira.html?hsLang=en-us)
- [ Learn RxJS ](https://www.bitovi.com/academy/learn-rxjs.html?hsLang=en-us)
- [ Giving a Training ](https://www.bitovi.com/academy/training.html?hsLang=en-us)
- [ Learn Technology Consulting ](https://www.bitovi.com/academy/learn-technology-consulting.html?hsLang=en-us)
- [ Learn React ](https://www.bitovi.com/academy/learn-react.html?hsLang=en-us)
- [ Learn NgRx ](https://www.bitovi.com/academy/learn-ngrx.html?hsLang=en-us)
- [ Learn Graphql ](https://www.bitovi.com/academy/learn-graphql.html?hsLang=en-us)
- [ Learn Docker ](https://www.bitovi.com/academy/learn-docker.html?hsLang=en-us)
- [ Learn CanJS ](https://www.bitovi.com/academy/learn-canjs.html?hsLang=en-us)
- [ Learn Angular ](https://www.bitovi.com/academy/learn-angular.html?hsLang=en-us) 
    - [ Why Angular? ](https://www.bitovi.com/academy/learn-angular/why-angular.html?hsLang=en-us)
    - [ Generate an App ](https://www.bitovi.com/academy/learn-angular/building-our-first-app.html?hsLang=en-us)
    - [ Creating Components ](https://www.bitovi.com/academy/learn-angular/creating-components.html?hsLang=en-us)
    - [ Creating Pipes ](https://www.bitovi.com/academy/learn-angular/creating-pipes.html?hsLang=en-us)
    - [ Adding Routing ](https://www.bitovi.com/academy/learn-angular/adding-routing.html?hsLang=en-us)
    - [ Creating Navigation ](https://www.bitovi.com/academy/learn-angular/creating-navigation.html?hsLang=en-us)
    - [ Fetching Data with Services ](https://www.bitovi.com/academy/learn-angular/restaurant-service.html?hsLang=en-us)
    - [ Rendering Data in Components ](https://www.bitovi.com/academy/learn-angular/pull-restaurant-data-into-view.html?hsLang=en-us)
    - [ Reactive Forms Data Binding ](https://www.bitovi.com/academy/learn-angular/state-city-options.html?hsLang=en-us)
    - [ Listening to Form Changes ](https://www.bitovi.com/academy/learn-angular/form-value-changes.html?hsLang=en-us)
    - [ Multi-parameter Services ](https://www.bitovi.com/academy/learn-angular/updating-service-params.html?hsLang=en-us)
    - [ Optional: Declarative State ](https://www.bitovi.com/academy/learn-angular/declarative-state.html?hsLang=en-us)
    - [ Writing Unit Tests ](https://www.bitovi.com/academy/learn-angular/writing-unit-tests.html?hsLang=en-us)
    - [ Creating Nested Routes ](https://www.bitovi.com/academy/learn-angular/nested-routes.html?hsLang=en-us)
    - [ Binding Component Data ](https://www.bitovi.com/academy/learn-angular/building-order-form.html?hsLang=en-us)
    - [ Creating Directives ](https://www.bitovi.com/academy/learn-angular/creating-directive.html?hsLang=en-us)
    - [ Order Service ](https://www.bitovi.com/academy/learn-angular/order-service.html?hsLang=en-us)
    - [ Order History Component ](https://www.bitovi.com/academy/learn-angular/order-history-component.html?hsLang=en-us)
    - [ Item Total Pipe ](https://www.bitovi.com/academy/learn-angular/item-total-pipe.html?hsLang=en-us)
    - [ Real Time Connections ](https://www.bitovi.com/academy/learn-angular/real-time-connection.html?hsLang=en-us)
    - [ Deploy App ](https://www.bitovi.com/academy/learn-angular/deploy-app.html?hsLang=en-us)
- [ Advanced TypeScript ](https://www.bitovi.com/academy/advanced-typescript.html?hsLang=en-us)

[View More Courses](https://www.bitovi.com/academy/#courses) [ ]

# Writing Unit Tests `page`

Write a unit test for a service in Angular.

## Overview

In this part, we will:

- Write a new `getRestaurant` method on our `RestaurantsService`
- Write a unit test for the `getRestaurant` method

## Problem

In the next section we’re going to be creating a restaurant detail view. We’ll need to have a `getRestaurant` method on our service that returns one restaurant from the list. Once the method is set up, write a unit test ensuring it makes the correct request and returns an object type of `Restaurant`.

## What you need to know

- How to write a unit test. Here’s a codeblock to get you started:
  
  ```
  it('should make a GET request to get a restaurant based on its slug', () => {
    
  });
  ```
- How to use `HttpTestingController` to test `HttpClient` calls.

## HttpTestingController

[Angular’s HTTP testing library](https://angular.io/guide/http-test-requests) was designed with a pattern in mind:

1. Make a request;
2. Expect one or more requests have (or not have) been made;
3. Perform assertions;
4. Resolve requests (flush);
5. Verify there are no unexpected requests.

Items `2` through `5` are covered by the Angular `HttpTestingController`, which enables mocking and flushing of requests.

The `HttpClientTestingModule` needs to be imported in the TestBed, and `HttpTestingController` can be injected using the TestBed for usage within test blocks.

For this exercise, both `HttpClientTestingModule` and `HttpTestingController` are already configured in `restaurant.service.spec.ts` file.

You can access `HttpTestingController` with the `httpTestingController` variable.

### Expecting a request to be made

`expectOne` method is handy when you want to test if a single request was made. `expectOne` will return a [TestRequest](https://angular.io/api/common/http/testing/TestRequest) object in case a matching request was made. If no request or more than one request was made, it will fail with an error.

```
const req = httpTestingController.expectOne('/api/states');
```

### Verifying the request

A `TestRequest`’s `request` property provides access to a wide variety of properties that may be used in a test. For example, if we want to ensure an HTTP GET request is made:

```
expect(req.request.method).toEqual('GET');
```

### Flushing request data

Without a specific command, outgoing requests will keep waiting for a response forever. You can resolve requests by using the `TestRequest`’s `flush` method

```
req.flush(mockStates);
```

### Avoiding the unexpected

When testing service methods in isolation, it’s better to ensure we don’t have unexpected effects. `HttpTestingController`’s `verify` method ensures there are no unmatched requests that were not handled.

```
httpTestingController.verify();
```

### Putting it all together

```
it('should make a GET request to get the states data', () => {
  const mockStates = {
    data: [{ name: 'California', short: 'CA' }]
  };

  service
    .getStates()
    .subscribe((states: State[]) => {
      expect(states).toEqual(mockStates);
    });

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

  expect(req.request.method).toEqual('GET');
  req.flush(mockStates);

  httpTestingController.verify();
});
```

## Setup

✏️ Update **src/app/restaurant/restaurant.service.ts** file with the new getRestaurants method:

```
import { HttpClient, HttpParams } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { environment } from '../../environments/environment';
import { Restaurant } from './restaurant';

export interface ResponseData<DataType> {
  data: DataType[];
}

export interface State {
  name: string;
  short: string;
}

export interface City {
  name: string;
  state: string;
}

@Injectable({
  providedIn: 'root'
})
export class RestaurantService {

  constructor(private httpClient: HttpClient) {}

  getRestaurants(
    state: string,
    city: string
  ): Observable<ResponseData<Restaurant>> {
    const params = new HttpParams()
      .set('filter[address.state]', state)
      .set('filter[address.city]', city);
    return this.httpClient.get<ResponseData<Restaurant>>(
      environment.apiUrl + '/restaurants',
      { params }
    );
  }

  getStates(): Observable<ResponseData<State>> {
    return this.httpClient.get<ResponseData<State>>(
      environment.apiUrl + '/states'
    );
  }

  getCities(state: string): Observable<ResponseData<City>> {
    const params = new HttpParams().set('state', state);
    return this.httpClient.get<ResponseData<City>>(
      environment.apiUrl + '/cities',
      { params }
    );
  }

  getRestaurant(slug: string): Observable<Restaurant> {
    return this.httpClient.get<Restaurant>(
      environment.apiUrl + '/restaurants/' + slug
    );
  }
}

```

## Solution

> Hint: you may use existing tests on the service as a guide.

Click to see the solution ✏️ Update **src/app/restaurant/restaurant.service.spec.ts**

```
import {
  HttpClientTestingModule,
  HttpTestingController,
} from '@angular/common/http/testing';
import { TestBed } from '@angular/core/testing';
import { Restaurant } from './restaurant';
import {
  City,
  ResponseData,
  RestaurantService,
  State,
} from './restaurant.service';

describe('RestaurantService', () => {
  let httpTestingController: HttpTestingController;
  let service: RestaurantService;

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

    httpTestingController = TestBed.inject(HttpTestingController);
    service = TestBed.inject(RestaurantService);
  });

  it('should be created', () => {
    expect(service).toBeTruthy();
  });

  it('should make a GET request to restaurants', () => {
    const mockRestaurants = {
      data: [
        {
          name: 'Brunch Place',
          slug: 'brunch-place',
          images: {
            thumbnail:
              'node_modules/place-my-order-assets/images/4-thumbnail.jpg',
            owner: 'node_modules/place-my-order-assets/images/2-owner.jpg',
            banner: 'node_modules/place-my-order-assets/images/2-banner.jpg',
          },
          menu: {
            lunch: [
              { name: 'Ricotta Gnocchi', price: 15.99 },
              { name: 'Garlic Fries', price: 15.99 },
              { name: 'Charred Octopus', price: 25.99 },
            ],
            dinner: [
              { name: 'Steamed Mussels', price: 21.99 },
              { name: 'Roasted Salmon', price: 23.99 },
              { name: 'Crab Pancakes with Sorrel Syrup', price: 35.99 },
            ],
          },
          address: {
            street: '2451 W Washburne Ave',
            city: 'Ann Arbor',
            state: 'MI',
            zip: '53295',
          },
          _id: 'xugqxQIX5rPJTLBv',
        },
        {
          name: 'Taco Joint',
          slug: 'taco-joint',
          images: {
            thumbnail:
              'node_modules/place-my-order-assets/images/4-thumbnail.jpg',
            owner: 'node_modules/place-my-order-assets/images/2-owner.jpg',
            banner: 'node_modules/place-my-order-assets/images/2-banner.jpg',
          },
          menu: {
            lunch: [
              { name: 'Beef Tacos', price: 15.99 },
              { name: 'Chicken Tacos', price: 15.99 },
              { name: 'Guacamole', price: 25.99 },
            ],
            dinner: [
              { name: 'Shrimp Tacos', price: 21.99 },
              { name: 'Chicken Enchilada', price: 23.99 },
              { name: 'Elotes', price: 35.99 },
            ],
          },
          address: {
            street: '13 N 21st St',
            city: 'Chicago',
            state: 'IL',
            zip: '53295',
          },
          _id: 'xugqxQIX5dfgdgTLBv',
        },
      ],
    };

    service
      .getRestaurants('IL', 'Chicago')
      .subscribe((restaurants: ResponseData<Restaurant>) => {
        expect(restaurants).toEqual(mockRestaurants);
      });

    const url =
      'http://localhost:7070/restaurants?filter%5Baddress.state%5D=IL&filter%5Baddress.city%5D=Chicago';
    // url parses to 'http://localhost:7070/restaurants?filter[address.state]=IL&filter[address.city]=Chicago'

    const req = httpTestingController.expectOne(url);

    expect(req.request.method).toEqual('GET');
    req.flush(mockRestaurants);

    httpTestingController.verify();
  });

  it('can set proper properties on restaurant type', () => {
    const restaurant: Restaurant = {
      name: 'Taco Joint',
      slug: 'taco-joint',
      images: {
        thumbnail: 'node_modules/place-my-order-assets/images/4-thumbnail.jpg',
        owner: 'node_modules/place-my-order-assets/images/2-owner.jpg',
        banner: 'node_modules/place-my-order-assets/images/2-banner.jpg',
      },
      menu: {
        lunch: [
          { name: 'Beef Tacos', price: 15.99 },
          { name: 'Chicken Tacos', price: 15.99 },
          { name: 'Guacamole', price: 25.99 },
        ],
        dinner: [
          { name: 'Shrimp Tacos', price: 21.99 },
          { name: 'Chicken Enchilada', price: 23.99 },
          { name: 'Elotes', price: 35.99 },
        ],
      },
      address: {
        street: '13 N 21st St',
        city: 'Chicago',
        state: 'IL',
        zip: '53295',
      },
      _id: 'xugqxQIX5dfgdgTLBv',
    };
    // will error if interface isn’t implemented correctly
    expect(true).toBe(true);
  });

  it('should make a GET request to states', () => {
    const mockStates = {
      data: [{ name: 'Missouri', short: 'MO' }],
    };

    service.getStates().subscribe((states: ResponseData<State>) => {
      expect(states).toEqual(mockStates);
    });

    const url = 'http://localhost:7070/states';
    const req = httpTestingController.expectOne(url);

    expect(req.request.method).toEqual('GET');
    req.flush(mockStates);

    httpTestingController.verify();
  });

  it('should make a GET request to cities', () => {
    const mockCities = {
      data: [{ name: 'Kansas City', state: 'MO' }],
    };

    service.getCities('MO').subscribe((cities: ResponseData<City>) => {
      expect(cities).toEqual(mockCities);
    });

    const url = 'http://localhost:7070/cities?state=MO';
    const req = httpTestingController.expectOne(url);
    expect(req.request.method).toEqual('GET');
    req.flush(mockCities);

    httpTestingController.verify();
  });

  it('should make a GET request to get a restaurant based on its slug', () => {
    const mockRestaurant = {
      name: 'Brunch Place',
      slug: 'brunch-place',
      images: {
        thumbnail: 'node_modules/place-my-order-assets/images/4-thumbnail.jpg',
        owner: 'node_modules/place-my-order-assets/images/2-owner.jpg',
        banner: 'node_modules/place-my-order-assets/images/2-banner.jpg',
      },
      menu: {
        lunch: [
          { name: 'Ricotta Gnocchi', price: 15.99 },
          { name: 'Garlic Fries', price: 15.99 },
          { name: 'Charred Octopus', price: 25.99 },
        ],
        dinner: [
          { name: 'Steamed Mussels', price: 21.99 },
          { name: 'Roasted Salmon', price: 23.99 },
          { name: 'Crab Pancakes with Sorrel Syrup', price: 35.99 },
        ],
      },
      address: {
        street: '2451 W Washburne Ave',
        city: 'Ann Arbor',
        state: 'MI',
        zip: '53295',
      },
      _id: 'xugqxQIX5rPJTLBv',
    };

    service
      .getRestaurant('brunch-place')
      .subscribe((restaurant: Restaurant) => {
        expect(restaurant).toEqual(mockRestaurant);
      });

    const url = 'http://localhost:7070/restaurants/brunch-place';
    const req = httpTestingController.expectOne(url);

    expect(req.request.method).toEqual('GET');
    req.flush(mockRestaurant);

    httpTestingController.verify();
  });
});

```

###### On this page...

###### Get help

If you need [help](https://discord.gg/J7ejFsZnJ4) with an exercise, please reach out to us on [Discord](https://discord.gg/J7ejFsZnJ4)

If you find a bug, please [create an issue](https://github.com/bitovi/academy/issues/new) or email [contact@bitovi.com](mailto:contact@bitovi.com?Subject=Training%20Suggestion)

## Join the Bitovi Newsletter

 Do you want to receive updates about new Bitovi Academy courses, Bitovi open source projects, and dev community news? If so, please provide your email. We won’t spam you or give it away for any reason!  
*By providing your email you agree to receive communications from Bitovi*

![](https://bitovi.github.io/academy/static/img/close.svg)

```json
{
     "@context": "http://schema.org",
     "@type": "Organization",
     "mainEntityOfPage":{
          "@type":"WebPage",
          "@id":"https://www.bitovi.com/academy/learn-angular/writing-unit-tests.html",
          "description": "Write a unit test for a service in Angular. 
"
     },
     "url": "http://bitovi.com",
     "logo": "https://www.bitovi.com/hubfs/bitovi-logo-23-1.svg",
     "image": "https://www.bitovi.com/hubfs/bitovi-logo-x2.png",
     "name": "Bitovi Web App Consulting",
     "alternateName": "Bitovi",
      "naics": "541511",
      "description": "Bitovi is a UX, UI design and front-end JavaScript development consulting company",
     "address": {
          "@type": "PostalAddress",
          "streetAddress": "1134 Pine Tree Lane ",
          "addressLocality": "Libertyville",
          "addressRegion": "IL",
          "addressCountry": "United States",
          "postalCode": "60048"
     },
     "areaServed": {
          "@type": "GeoCircle",
          "geoMidpoint": {
               "@type": "GeoCoordinates",
               "latitude": "41.8781",
               "longitude": "87.6298"
          },
          "geoRadius": "5000 km"
     },
     "telephone": "312-620-0386",
     "email": "contact@bitovi.com",
     "sameAs": [
          "https://www.facebook.com/BitoviLLC/",
          "https://twitter.com/bitovi",
          "https://www.linkedin.com/company/bitovi"
     ]
}
```