Testing Services in Angular

See how to test a service in Angular and make sure it interacts with APIs. Check the API call and return the data.


This content originally appeared on Telerik Blogs and was authored by Dany Paredes

See how to test a service in Angular and make sure it interacts with APIs. Check the API call and return the data.

When we build applications with Angular, the components have the responsibility to display data and allow user interaction. In some cases, the data comes from services as static data, or in the real world, it comes from an API.

In the previous article, we learned how to test our components, mocking with fake data. However, the app is more than just the components—what about the service?

Today we’re going to learn how to test our services so they interact with APIs. We’ll learn how to make sure they call the API and return the data.

Let’s dive in!

Setting Up the Project

First, clone the project by running the following command in the terminal:

git clone https://github.com/danywalls/testing-kendo-store.git
Cloning into 'testing-kendo-store'...
remote: Enumerating objects: 149, done.
remote: Counting objects: 100% (149/149), done.
remote: Compressing objects: 100% (90/90), done.
remote: Total 149 (delta 86), reused 115 (delta 54), pack-reused 0
Receiving objects:  93% (139/149
Receiving objects:  95% (142/149)
Receiving objects: 100% (149/149), 158.84 KiB | 2.24 MiB/s, done.
Resolving deltas: 100% (86/86), done.

Next, install all dependencies for the project by running npm install in the testing-kendo-store directory.

cd testing-kendostore
 npm i
 
added 1120 packages, and audited 1121 packages in 6s
 
133 packages are looking for funding
  run `npm fund` for details
 
found 0 vulnerabilities

Run the command npm run test to confirm everything works.

Output location: C:\Users\DPAREDES\Desktop\articles\testing-kendo-store\dist\test-out

Application bundle generation complete. [1.294 seconds]


dist\test-out\browser\app.component.spec.js:

 Browser logs:
Tests passed!

Chrome: |██████████████████████████████| 1/1 test files | 1 passed, 0 failed

Finished running tests in 0.8s, all tests passed! 

Testing the Service

Services are easy to test. They are classes with or without dependencies. Similar to the components, we can rely on TestBed to handle the service and its dependencies.

Testbed allows us to provide the dependencies. Angular also provides other utilities to simplify, like HttpTestingController and provideHttpClientTesting, to make testing and mocking requests easier.

Read more about HttpTestingController.

Before we start, let’s analyze our code. Open the product.service.ts. We have a property to make the request to the API and expose the products$ observable.

@Injectable({
  providedIn: 'root'
  })
  export class ProductsService {
    private API = 'https://fakestoreapi.com/products'
    private http = inject(HttpClient)
    public products$ = this.http.get<Product[]>(this.API);
  }

The product.service.ts needs the httpClient. Also, it has a private property API pointing to the API, so we need to make sure it calls the API with a GET request.

We must configure the TestBed with the following points:

  • Provide a mock for the HttpClient.
  • Use the HttpTestingController to mock the response.
  • Reuse the MOCK_PRODUCTS example data.

Let’s get started!

Remember, we explained Jasmine basics in the previous article.

Writing the Test

Open the products.service.spec.ts, it contains the following code that does not looks like a real test:

describe('ProductsService', () => {
it('should be create in next article ', () => {
  expect(true).toBeTruthy();
  });
});

It’s time to write the test for our ProductService, in the products.service.spec.ts file so we’re going to do the same approach, declare two variables for the productService and httpTestingController.

import {ProductsService} from "./products.service";
import {HttpTestingController} from "@angular/common/http/testing";

describe('ProductsService', () => {
  let productService: ProductsService;
  let httpTestingController: HttpTestingController;
});

Next, using the beforeEach lifecycle hook, and using Testbed, we provide the provideHttpClient, provideHttpClientTesting and ProductService.

Because the Testbed takes care of the DI, we use TestBed.inject() to set the httpTestingController and ProductService.

import {ProductsService} from "./products.service";
import {HttpTestingController, provideHttpClientTesting} from "@angular/common/http/testing";
import {TestBed} from "@angular/core/testing";
import {provideHttpClient} from "@angular/common/http";

describe('ProductsService ', () => {
  let productService: ProductsService;
  let httpTestingController: HttpTestingController;
  beforeEach(() => {
    TestBed.configureTestingModule(
      {
        providers: [
          provideHttpClient(),
          provideHttpClientTesting(),
          ProductsService,
        ],
      }
    )
    productService = TestBed.inject(ProductsService);
    httpTestingController = TestBed.inject(HttpTestingController);
  });
});

Next, write a test to validate we have an instance of ProductService.

it('should create an instance', () => {
    expect(productService).toBeTruthy();
  })

Run the test again and it’s perfect!

Application bundle generation complete. [1.258 seconds]
dist\test-out\browser\products.service.spec.js:

 Browser logs:
Tests passed!

dist\test-out\browser\app.component.spec.js:

 Browser logs:
Tests passed!

Chrome: |██████████████████████████████| 2/2 test files | 2 passed, 0 failed

Finished running tests in 0.9s, all tests passed! 

OK, we have our basic test to create an instance of ProductService, but it’s not really adding much value. I want to check that $products returns a list of observables from the Http without requesting the real API.

Let’s write a real test where we verify the request data using a GET request and return a mock example.

Testing Http Requests

Let’s test our service. First, add a new test like should make an HTTP request. Our test uses the products$ observable property and subscribes to the observable.

Inside, we use the Jasmine matcher expect and toEqual to confirm the response to be equal to MOCK_PRODUCTS.

it('should make an HTTP request', fakeAsync (() => {
  productService.products$.subscribe((response) => {
    expect(response).toEqual(MOCK_PRODUCTS)
  })    
}

Because the httpTestingController is listening to the request, we expect it to call the https://fakestoreapi.com/products URL. So, we call the expectOne method and store the request in the variable req, then call the httpTestingController.verify() method to confirm the request was to the expected URL.

const req = httpTestingController.expectOne('https://fakestoreapi.com/products');
httpTestingController.verify();

Finally, we confirm the request is a GET and flush the mock example data.

expect(req.request.method).toBe('GET');
req.flush(MOCK_PRODUCTS)
flush()

The final code looks like:

import { fakeAsync, flush, TestBed } from '@angular/core/testing';
import { MOCK_PRODUCTS } from '../tests/mock';

...

it('should make an HTTP request', fakeAsync(() => {
productService.products$.subscribe((response) => {
  expect(response).toEqual(MOCK_PRODUCTS);
  });
  const req = httpTestingController.expectOne(
  'https://fakestoreapi.com/products',
  );
  httpTestingController.verify();
  
  expect(req.request.method).toBe('GET');
  req.flush(MOCK_PRODUCTS);

  flush();
}));

Save changes and run your tests again and tada!!

Output location: C:\Users\DPAREDES\Desktop\articles\testing-kendo-store\dist\test-out

Application bundle generation complete. [1.220 seconds]
dist\test-out\browser\products.service.spec.js:
 Browser logs:
Tests passed!
dist\test-out\browser\app.component.spec.js:
 Browser logs:
Tests passed!
  
Chrome: |██████████████████████████████| 2/2 test files | 4 passed, 0 failed

Finished running tests in 0.9s, all tests passed! 

Yes, we have our application with tests for both our components and services!! I can rest easy or make new changes in the app.

What About Testing User Interactions?

Yes, that’s a good question. What if I want to test an interaction? For example, when the user clicks on the article, the purchase button appears?

Well, that’s the moment when we need to delve deeper into learning about Jasmine and using CSS selectors to query elements, or we can utilize libraries like Angular Testing Library. Alternatively, an easy yet powerful alternative is to use Progress Telerik Test Studio.

Telerik Test Studio makes it easy to check that applications running in a web browser behave as expected across different browsers and simplifies observing consistent web UI on multiple browsers or browser versions, for clean user interactions, form validation, API calls and more.

Visit the Progress Telerik DevCraft page to learn about bundle options that include Test Studio and Kendo UI for Angular.

Conclusion

We’ve learned how easy it is to test our service and how TestBed helps us to configure the dependencies, we register the dependencies on TestBed and it handles DI to provide instances to us.

We played with the HttpTestingController, which helps us to mock the response and avoid making calls to the real server. We can effectively test service logic without relying on external dependencies.

I hope this article helps you get started with testing in Angular. If you want more articles like this, leave a comment !


This content originally appeared on Telerik Blogs and was authored by Dany Paredes


Print Share Comment Cite Upload Translate Updates
APA

Dany Paredes | Sciencx (2024-07-10T08:02:57+00:00) Testing Services in Angular. Retrieved from https://www.scien.cx/2024/07/10/testing-services-in-angular/

MLA
" » Testing Services in Angular." Dany Paredes | Sciencx - Wednesday July 10, 2024, https://www.scien.cx/2024/07/10/testing-services-in-angular/
HARVARD
Dany Paredes | Sciencx Wednesday July 10, 2024 » Testing Services in Angular., viewed ,<https://www.scien.cx/2024/07/10/testing-services-in-angular/>
VANCOUVER
Dany Paredes | Sciencx - » Testing Services in Angular. [Internet]. [Accessed ]. Available from: https://www.scien.cx/2024/07/10/testing-services-in-angular/
CHICAGO
" » Testing Services in Angular." Dany Paredes | Sciencx - Accessed . https://www.scien.cx/2024/07/10/testing-services-in-angular/
IEEE
" » Testing Services in Angular." Dany Paredes | Sciencx [Online]. Available: https://www.scien.cx/2024/07/10/testing-services-in-angular/. [Accessed: ]
rf:citation
» Testing Services in Angular | Dany Paredes | Sciencx | https://www.scien.cx/2024/07/10/testing-services-in-angular/ |

Please log in to upload a file.




There are no updates yet.
Click the Upload button above to add an update.

You must be logged in to translate posts. Please log in or register.