【问题标题】:Angular How to test a Component which requires a LocationAngular 如何测试需要位置的组件
【发布时间】:2018-06-20 11:40:22
【问题描述】:

您好,感谢您抽出宝贵时间!

我正在学习如何使用 Angular,并且我有兴趣学习如何测试其组件。

目前我正在苦苦挣扎,因为我已经完成了 Angular 页面的英雄之旅教程,并且我正在测试代码以更好地理解它。

关键是我正在测试 hero-details 组件的代码是:

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();
  }
}

我的测试试图证明 getHero() 在创建 hero-details 组件后被调用:

 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();



      });
    });

我面临的困难是当我尝试创建一个新的 Location 时,它是 Hero-datail 组件的构造函数的必需参数。

第一个Location的参数是一个PlatformStrategy,所以我用provider来构建它。此外,提供者需要一个看起来很抽象的 PlatformLocation(),所以我选择了我能找到的唯一实现,即 BrowserPlatformLocation。

在所有这些过程之后,测试执行说:

而且浏览器永远不会结束加载套件:

这里奇怪的是,IDE 确实找到了这些模块,因为我可以导航到它们。

此外,如果我注释掉该测试,该套件运行良好:

另外我也读过:

我怎样才能以正确的方式对其进行测试?我在哪里可以找到有关正确进行此类测试的更多信息?这个测试如何轻松模拟 Location 参数?

感谢您阅读本文

【问题讨论】:

    标签: javascript angular unit-testing jasmine


    【解决方案1】:

    Location是内置服务,不需要实例化,mock一下即可:

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

    然后在您的providers 数组中:

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

    然后在您的测试中调用组件goBack 方法,然后使用Injector 获取您的服务存根的实例,如下所示:

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

    然后只是测试,back 函数已被调用:

    expect(location.back).toHaveBeenCalled();
    

    这应该可以解决问题。据我所知,这是处理内置服务的最佳方式,您不需要测试它们(Angular 团队已经这样做了),只需模拟它们并确保它们被正确调用

    【讨论】:

    • 我使用 expect((location as any).back).toHaveBeenCalledTimes(1);避免编译错误
    • “位置”类型上不存在属性“后退”。
    • @MiomirDancevic 那么你的“位置”有误,请确保你使用来自@angular/common的课程
    【解决方案2】:
         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');
       }));
    

    使用来自 @angular/router/testing 的 SpyLocation

    【讨论】:

    • 那是什么角度的版本? SpyLocation 不存在。
    • 也许您在查看 @angular/core,但它在 Angular 11 中是 @angular/common。 import { SpyLocation } from '@angular/common/testing';
    • 这是一个很好的答案,但据我了解这里的文档 (angular.io/api/router/testing/RouterTestingModule) 您不必在 providersarray 中明确定义您的 Location 提供程序,因为 RouterTestingModule 已经提供它。在您的情况下,您使用相同的代码覆盖它。我也测试过了。
    【解决方案3】:

    如果我是你,我不会费心从头开始创建测试:CLI 会创建预制测试。

    在这些测试中,有一个 TestBed,用于设置一个测试模块来测试您的组件。如果你使用它,你只需要导入一个路由器测试模块。

    这会给出这样的结果:

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

    就这样,你的整个路由策略就被嘲笑了。

    【讨论】:

      猜你喜欢
      • 2017-08-17
      • 1970-01-01
      • 1970-01-01
      • 2021-02-22
      • 2022-01-15
      • 2016-10-31
      • 2020-09-04
      • 1970-01-01
      • 2019-01-21
      相关资源
      最近更新 更多