Angular 如何测试需要位置的组件

Posted

技术标签:

【中文标题】Angular 如何测试需要位置的组件【英文标题】:Angular How to test a Component which requires a Location 【发布时间】: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 确实找到了这些模块,因为我可以导航到它们。

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

另外我也读过:

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

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

感谢您阅读本文

【问题讨论】:

【参考方案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();
));

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

【讨论】:

以上是关于Angular 如何测试需要位置的组件的主要内容,如果未能解决你的问题,请参考以下文章

Angular 2 单元测试错误:无法解析“RequestOptions”的所有参数

如何获得 Angular 5 组件元素的位置?

Angular v14 现已推出

如何通过将 id 存储在变量中来渲染组件?

获取 Angular 组件的滚动位置

Angular如何测试组件方法?