How to test map and tap pipe from RXJS in Angular

11,290

Yes, getting 100% coverage probably is not the best idea but is a good goal to shoot for.

As I see your service does HTTP requests, follow this guide for the testing: https://medium.com/better-programming/testing-http-requests-in-angular-with-httpclienttestingmodule-3880ceac74cf.

And yes, you will have to mock loggingService;

Something like this:

import { TestBed } from '@angular/core/testing';
import { CoursesService } from './courses.service';
import { HttpClientTestingModule,
         HttpTestingController } from '@angular/common/http/testing';
... // the rest of your imports

describe('TemplateService', () => {
  // We declare the variables that we'll use for the Test Controller and for our Service
  let httpTestingController: HttpTestingController;
  let service: TemplateService;
  let mockLoggingService: any;
  
  beforeEach(() => {
    // 'loggingService' is for your own sanity and the array are all of the public methods of `loggingService`
    mockLoggingService = jasmine.createSpyObj('loggingService', ['logger']);
    TestBed.configureTestingModule({
      providers: [
                  TemplateService,
                  // every time the test asks for LoggingService, supply this mock
                  { provide: LoggingService, useValue: mockLoggingService },
     ],
      imports: [HttpClientTestingModule]
    });

    // We inject our service (which imports the HttpClient) and the Test Controller
    httpTestingController = TestBed.get(HttpTestingController);
    service = TestBed.get(TemplateService);
  });

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

  // Angular default test added when you generate a service using the CLI
  it('should be created', () => {
    expect(service).toBeTruthy();
  });

  it('should make a get call for getTemplates and log', () => {
    const mockTemplates: Template[] = [/* you know how a template should look like so mock it*/];
   // make HTTP call take flight
    service.getTemplates().subscribe(templates => {
      expect(templates).toEqual(mockTemplates);
      expect(mockLoggingService.logger).toHaveBeenCalledWith(`User viewed templates: ${templates}`);
    });
   
   // have a handle on the HTTP call that is about to take flight
   const req = httpTestingController.expectOne(/* put the unique url of this method's get request that only you know of */);

   expect(req.request.method).toEqual('GET');
   // send this request for the next HTTP call
   req.flush(mockTemplates);
  });

  it('should make a get call for getTemplateById and log', () => {
    const mockTemplate: Template = {/* you know how a template should look like so mock it*/};
   // make HTTP call take flight
    service.getTemplateById(1).subscribe(template => {
      expect(template).toEqual(mockTemplate);
      expect(mockLoggingService.logger).toHaveBeenCalledWith(`User viewed template: ${template}`);
    });
   
   // have a handle on the HTTP call that is about to take flight
   const req = httpTestingController.expectOne(/* put the unique url of this method's get request that only you know of */);

   expect(req.request.method).toEqual('GET');
   // send this request for the next HTTP call
   req.flush(mockTemplates);
  });
});

Side Note: Your maps are doing nothing in both functions and they can be removed. They are just returning what they are receiving and are not transforming anything.

Share:
11,290
ineedtoknow
Author by

ineedtoknow

Hi, I'm a software application developer for a health insurance company. I'm full-stack, but I like to focus on frontend when I can. I mainly use Angular, Go lang, and MongoDB for work. I also work with docker, kubernetes, and helm for containerization and deployments. I use this to get help on my issues and errors and to try to help others (when I'm fast enough, haha). Hope I can lend a hand and learn a lot from others here!

Updated on June 04, 2022

Comments

  • ineedtoknow
    ineedtoknow almost 2 years

    I am wanting to test my code below, but I'm not sure how to test the map and tap (from RXJS) functions. Should I make a mock, use a spy?

    Should I even really test these? I have a habit of getting 100% coverage for only achieving that goal (100% coverage), but I'm learning that 100% isn't always needed or beneficial. But in this case, map and tap are pretty crucial to the function. I'd really appreciate any advice, thank you.

    I am using Angular 9.

    The red lines are untested.

    enter image description here

    • Alexander Staroselsky
      Alexander Staroselsky about 4 years
      Yes, you’ll need to stub HttpClient calls which is covered in the docs on testing. You can then create a spy for logger. Start with doing that. That being said, map operator is unnecessary in your code.
    • Frederick
      Frederick about 4 years
      You can look at using angular.io/api/core/testing/fakeAsync as well to make sure that your async work is completed during your test.
  • ineedtoknow
    ineedtoknow about 4 years
    Thanks for the answer and article, and I appreciate your note on not needing maps, I didn't realize that.