如何在 (jasmine + karma) 中为以下方法编写测试,该方法在构造函数中注入了 ComponentFactoryResolver 和 ApplicationRef

Posted

技术标签:

【中文标题】如何在 (jasmine + karma) 中为以下方法编写测试,该方法在构造函数中注入了 ComponentFactoryResolver 和 ApplicationRef【英文标题】:How do I write a test for the following method in (jasmine + karma) that has ComponentFactoryResolver & ApplicationRef injected in the constructor 【发布时间】:2022-01-12 06:48:50 【问题描述】:

我正在学习 jasmine 和 karma 的角度测试。我在我的项目中遇到了这个 showBanner() 方法,不知道如何进一步进行。这是一个服务方法,它使用给定的参数创建横幅组件,并在构造函数中注入 ComponentFactoryResolver 和 ApplicationRef。

我做了一些研究,但找不到与此相关的任何示例。如何模拟构造函数依赖项,特别是 ApplicationRef。我在某处读到它没有必要在测试平台的提供程序数组中模拟 ApplicationRef 但我不断收到此错误-> TypeError: Cannot read properties of undefined (reading 'instance') for the showBanner方法第 1 行,访问 appliationRef 的 components 属性。

这里是完整的服务方法

import  ApplicationRef, ComponentFactoryResolver, Injectable, ViewContainerRef  from "@angular/core";
import  BannerComponent  from "src/app/components/banner/banner.component";
import  BannerSeverity  from "src/app/utils/utils";

@Injectable(
  providedIn: 'root'
)
export class BannerService 
  private resolver: ComponentFactoryResolver;                                 // Reference to component factory resolver
  banners : Array<any> = [];                                                  // Array to store all the banners

  /**
   * Constructor
   */
  constructor(
    resolver: ComponentFactoryResolver,
    private applicationRef: ApplicationRef) 
      this.resolver = resolver;
  

  /**
   * Function called to show the banner with the specified configuration
   */
  public showBanner(severity: BannerSeverity, message: string, dismissible?: boolean, appendTo?: ViewContainerRef, moreInfo?: string ): void 
    let globalViewContainerRef = this.applicationRef.components[0].instance.viewContainerRef;
    let factory = this.resolver.resolveComponentFactory(BannerComponent);
    let component = null;
    if (appendTo) 
      component = appendTo.createComponent(factory);
     else if (globalViewContainerRef) 
      component = globalViewContainerRef.createComponent(factory);
    
    if (component) 
      let banner = component.instance;
      banner.severity = BannerSeverity[severity];
      banner.message = message;
      banner.dismissible = dismissible;
      banner.moreInfo = moreInfo;
      banner.dismissed.subscribe(() => 
        component.destroy();
      );
      this.banners.push(banner);
    
  


这是我的测试文件

import  ApplicationRef, ComponentFactoryResolver  from "@angular/core";
import  fakeAsync, flush, TestBed  from "@angular/core/testing";
import  BannerSeverity  from "src/app/utils/utils";
import  getTypicalSetup  from "src/testing/typical-setup";
import  BannerService  from "./banner.service";

/**
 * Test cases for banner service.
 */
describe('BannerService', () => 
  const mock = getTypicalSetup();
  let bannerService: BannerService;
  let BANNERS;
  let componentFactoryResolverSpy;

  /**
   * Run some shared setup before each of the specs.
   */
  beforeEach(fakeAsync(() => 
    componentFactoryResolverSpy = jasmine.createSpyObj<ComponentFactoryResolver>('ComponentFactoryResolver', ['resolveComponentFactory']);
    //Configure the test bed.
    TestBed.configureTestingModule(
      imports: [...mock.imports],
      providers: [
        ...mock.providers,
        BannerService,
         provide: ComponentFactoryResolver, useValue: componentFactoryResolverSpy ,
      ],
    );
    bannerService = TestBed.inject(BannerService);
    //Banners array with wasDismissed property.
    BANNERS = [
       wasDismissed: false ,
       wasDismissed: false ,
    ];
  ));

  /**
   * Test to ensure the service is created.
   */
  it('should be created', () => 
    expect(bannerService).toBeDefined();
  );

  /**
   * Test to ensure that the showBanner method creates a banner
   * based on various parameters.
   */
  describe('#showBanner', () => 
    it('should create a banner with severity and message parameters passed', fakeAsync(() => 
      //Arrange
      const severity: BannerSeverity = BannerSeverity.critical;
      const message: string = "Test Message";

            //Act
      bannerService.showBanner(severity, message);
            flush();

            //Assert
      expect(bannerService.banners[0]).toBeDefined();
        ));
  );
);

【问题讨论】:

【参考方案1】:

您可以像 ComponentFactoryResolver 一样模拟它。

import  ApplicationRef, ComponentFactoryResolver  from "@angular/core";
import  fakeAsync, flush, TestBed  from "@angular/core/testing";
import  BannerSeverity  from "src/app/utils/utils";
import  getTypicalSetup  from "src/testing/typical-setup";
import  BannerService  from "./banner.service";

/**
 * Test cases for banner service.
 */
describe('BannerService', () => 
  const mock = getTypicalSetup();
  let bannerService: BannerService;
  let BANNERS;
  let componentFactoryResolverSpy: jasmine.SpyObj<ComponentFactoryResolver>;
  // add this line
  let applicationRefSpy: jasmine.SpyObj<ApplicationRef>;

  /**
   * Run some shared setup before each of the specs.
   */
  beforeEach(fakeAsync(() => 
    componentFactoryResolverSpy = jasmine.createSpyObj<ComponentFactoryResolver>('ComponentFactoryResolver', ['resolveComponentFactory']);
    // mock how you like
    applicationRefSpy = jasmine.createSpyObj<ApplicationRef>('ApplicationRef', ,  components: [ instance:  viewContainerRef:   ] );
    //Configure the test bed.
    TestBed.configureTestingModule(
      imports: [...mock.imports],
      providers: [
        ...mock.providers,
        BannerService,
         provide: ComponentFactoryResolver, useValue: componentFactoryResolverSpy ,
        // add mock
         provide: ApplicationRef, useValue: applicationRefSpy ,
      ],
    );
    bannerService = TestBed.inject(BannerService);
    //Banners array with wasDismissed property.
    BANNERS = [
       wasDismissed: false ,
       wasDismissed: false ,
    ];
  ));

  /**
   * Test to ensure the service is created.
   */
  it('should be created', () => 
    expect(bannerService).toBeDefined();
  );

  /**
   * Test to ensure that the showBanner method creates a banner
   * based on various parameters.
   */
  describe('#showBanner', () => 
    it('should create a banner with severity and message parameters passed', fakeAsync(() => 
      //Arrange
      const severity: BannerSeverity = BannerSeverity.critical;
      const message: string = "Test Message";

            //Act
      bannerService.showBanner(severity, message);
            flush();

            //Assert
      expect(bannerService.banners[0]).toBeDefined();
        ));
  );
);

查看此链接:https://***.com/a/64560773/7365461,了解如何使用 createSpyObj 模拟属性。

【讨论】:

以上是关于如何在 (jasmine + karma) 中为以下方法编写测试,该方法在构造函数中注入了 ComponentFactoryResolver 和 ApplicationRef的主要内容,如果未能解决你的问题,请参考以下文章

在Jasmine Unit Test中为PhantomJS配置浏览器语言

如何安装和使用Karma-Jasmine

Karma-Jasmine之安装与实例详解

在Visual Studio 2013中使用Jasmine + Karma进行AngularJS测试

如何在 Angular 7 中使用 Karma/Jasmine 为 App_Initializer 编写单元测试用例

Karma 与测试框架 Jasmine、Mocha、QUnit [关闭]