Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular How to test a Component which requires a Location

Hello and thank you for your time!

I am learning how to use Angular and I am interested in learning how to test its Components.

Currently I am struggling because I have done the Tour of Heroes tutorial of the Angular page and I am testing the code to understand it better.

The point is that I am testing hero-details component which code is:

import {Component, OnInit, Input} from '@angular/core';
import {ActivatedRoute} from '@angular/router';
import {MyHeroService} from '../hero-service/my-hero.service';
import {Location} from '@angular/common';
import {Hero} from '../Hero';

@Component({
  selector: 'app-hero-details',
  templateUrl: './hero-details.component.html',
  styleUrls: ['./hero-details.component.css']
})
export class HeroDetailsComponent implements OnInit {
  @Input() hero: Hero;

  constructor(private route: ActivatedRoute,
              private myHeroService: MyHeroService,
              private location: Location) {
  }

  ngOnInit(): void {
    this.getHero();
  }

  getHero(): void {
    const id = +this.route.snapshot.paramMap.get('id');
    this.myHeroService.getHero(id)
      .subscribe(hero => this.hero = hero);
  }

  goBack(): void {
    this.location.back();
  }
}

And my test tries to prove that getHero() is called after creating the hero-details component:

 import {HeroDetailsComponent} from './hero-details.component';
    import {ActivatedRoute} from '@angular/router';
    import {MyHeroService} from '../hero-service/my-hero.service';
    import {MessageService} from '../message.service';
    import {Location} from '@angular/common';
    import {provideLocationStrategy} from '@angular/router/src/router_module';
    import {BrowserPlatformLocation} from '@angular/platform-browser/src/browser/location/browser_platform_location';


    describe('heroDetails', () => {
      it('should call getHero after being created', () => {
        const heroDetailsComponent = new HeroDetailsComponent(new ActivatedRoute(),
          new MyHeroService(new MessageService([])),
          new Location(provideLocationStrategy(new BrowserPlatformLocation(['anyParameter']), '/')));
        spyOn(heroDetailsComponent, 'getHero');

        heroDetailsComponent.ngOnInit();

        expect(heroDetailsComponent.getHero()).toHaveBeenCalled();



      });
    });

The difficulty I am facing is when I try to create a new Location which is a required parameter for the Hero-datail component's constructor.

The first Location's parameter is a PlatformStrategy, so then I used the provider to build it. Also, the provider needs a PlatformLocation() which looks like is abstract so then I chose the only implementation I could find which is BrowserPlatformLocation.

After all that process, the test execution says: enter image description here

And the browser never ends loading the suite: enter image description here

The strange thing here is that the IDE indeed finds those modules because I can navigate to them.

Also if I comment out that test, the suite works well:

enter image description here

Additionaly I have also read:

    1. https://angular.io/api/common/Location
    2. https://www.tektutorialshub.com/location-strategies-angular/
    3. https://angular.io/tutorial/toh-pt5

How could I test it in a correct way? Where could I find more information about doing this type of tests properly? How could do this test to mock easily that Location parameter?

Thank you for reading this

like image 988
Enoy Avatar asked Jan 11 '18 07:01

Enoy


2 Answers

     beforeEach(async(() => {
       TestBed.configureTestingModule({
         declarations: [ HeroDetailsComponent ],
        providers: [ MyHeroService ],
         imports: [ RouterTestingModule ],
       providers: [{ provide: Location, useClass: SpyLocation }]
       })
         .compileComponents();
     }));

  it('should logout from application', async(() => {
         const location: Location = TestBed.get(Location);

         expect(location.href).toContain('blablabla url');
   }));

Use SpyLocation from @angular/router/testing

like image 194
Maclean Pinto Avatar answered Nov 03 '22 01:11

Maclean Pinto


Location is a built-in service, you do not need to instantiate it, just mock it:

const locationStub = {
    back: jasmine.createSpy('back')
}

Then in your providers array:

providers: [ ..., {provide: Location, useValue: locationStub} ],

Then in your test just call the components goBack method, then use the Injector to get the instance of your service stub, like this:

const location = fixture.debugElement.injector.get(Location);

And then just test, that the back function has been called:

expect(location.back).toHaveBeenCalled();

This should solve the problem. This is, as far as I have seen, the best way to deal with the built-in services, you don't need to test them (Angular team did that already), just mock them and make sure they have been called correctly

like image 24
Armen Vardanyan Avatar answered Nov 03 '22 01:11

Armen Vardanyan