测试依赖于服务的管道

44

我有一段可以将HTML进行清洗的代码:

import { Pipe, PipeTransform } from '@angular/core';
import { DomSanitizer } from '@angular/platform-browser';

@Pipe({
    name: 'sanitiseHtml'
})

export class SanitiseHtmlPipe implements PipeTransform {

constructor(private _sanitizer: DomSanitizer) {}

    transform(value: any): any {
      return this._sanitizer.bypassSecurityTrustHtml(value);
    }

}

我想要以下方式进行测试:

describe('Pipe: Sanatiser', () => {
    let pipe: SanitiseHtmlPipe;

    beforeEach(() => {
        pipe = new SanitiseHtmlPipe(new DomSanitizer());
    });

    it('create an instance', () => {
        expect(pipe).toBeTruthy();
    }); 
});

DomSanatizer是一个抽象类,通过将其传递到构造函数中,TypeScript会自动装配它:

constructor(private _sanitizer: DomSanitizer) {}

目前我得到了TypeScript错误:

无法创建抽象类'DomSanitizer'的实例。

有人知道在Angular中将依赖项传递到构造函数中时,TypeScript会执行什么操作吗?或者测试此类东西的方法是什么?


请查看 https://github.com/angular/angular/blob/master/packages/platform-browser/src/security/dom_sanitization_service.ts ,您应该注入一个 DomSanitizerImpl 实例。 - Jota.Toledo
它无法找到它:模块未找到:错误:无法解析'@angular/platform-browser/src/security/dom_sanitization_service' - Ben Taliadoros
似乎它不是公共API的一部分。 - Jota.Toledo
https://dev59.com/1Jnga4cB1Zd3GeqPgPYV - Jota.Toledo
3个回答

75

由于管道中的 DI(依赖注入),您需要配置一个测试环境(测试床)来解决依赖关系:

import { BrowserModule, DomSanitizer } from '@angular/platform-browser';
import { inject, TestBed } from '@angular/core/testing';

describe('SanitiseHtmlPipe', () => {
  beforeEach(() => {
    TestBed
      .configureTestingModule({
        imports: [
          BrowserModule
        ]
      });
  });

  it('create an instance', inject([DomSanitizer], (domSanitizer: DomSanitizer) => {
    let pipe = new SanitiseHtmlPipe(domSanitizer);
    expect(pipe).toBeTruthy();
  })); 
});

我还需要添加 import { BrowserModule, DomSanitizer } from '@angular/platform-browser'; - Csaba Toth
1
"@codeepic 'new SanitiseHtmlPipe(new DomSanitizer())' 和你上面注入的方式有什么区别?'new' 无法使用,因为该类是抽象的。" - Jota.Toledo
1
@codeepic听起来并不复杂。我不确定你所说的“模拟类和其方法3次”是什么意思,但我的方法是提供一个模拟对象,然后用jasmine对“getFullDate()”方法进行监视,并返回测试所需的内容。欢迎开新问题并在上面标记我。 - Jota.Toledo
1
@Jota.Toledo 感谢你提供的spyreturnValue小贴士,我之前并不知道。现在我需要查看Jasmine文档以了解其他有用的东西。我已经重构了我的单元测试。 - codeepic
非常好的答案。这是唯一一个能够与我的管道一起使用DOCUMENT作为注入令牌的答案。 - PeterB
显示剩余4条评论

20

如果有人想要重用Pipe的构造函数,可以使用TestBed来实现相同的结果:

  let pipe: SafeHtmlPipe;
  let sanitized: DomSanitizer

  beforeEach(async() => {
    TestBed.configureTestingModule({
      providers: [DomSanitizer]
    });
    sanitized = TestBed.get(DomSanitizer);
    pipe = new SafeHtmlPipe(sanitized);
  });

  it('create an instance', () => {
    expect(pipe).toBeTruthy();
  });

3
但似乎“sanitized”不可用:“this.sanitizer.bypassSecurityTrustHtml不是一个函数”。这是当我尝试使用该管道时出现的错误。 - martin-g
2
据我所知,这不是方法的问题,因为由于某种原因,这个函数和其他函数都是静态的,因此你不能在测试中像那样调用它们。如果我没错的话,如果你想测试方法调用,你需要像这样模拟它们:<pre><code>TestBed.configureTestingModule({ imports: [BrowserModule], providers: [ {provide: DomSanitizer, useValue: { sanitize: () => 'safeString', bypassSecurityTrustHtml: () => 'safeString' } } ] });</code></pre> - Alain Boudard
抱歉,抽象方法不是静态的。 - Alain Boudard
1
有没有一种方法可以注入实际用于非测试代码中的 DomSanitizerImpl? - martin-g

4

如果您想模拟整个提供程序,并且不想使用构造函数,那么这就是我的做法(使用Jest但用您的常规jasmine.createSpyObj替换spy)

规范

describe("MyPipe", () => {
  let pipe: MyPipe;
  const myServiceSpy = { myFunction: jest.fn() };

  beforeEach(() => {
    jest.clearAllMocks();
    TestBed.configureTestingModule({
      providers: [
        MyPipe,
        {
          provide: MyService,
          useValue: myServiceSpy
        }
      ]
    });

    pipe = TestBed.inject(myPipe);
  });

  it("create an instance", () => {
    expect(pipe).toBeTruthy();
  });
});

管道

@Pipe({
  name: "myPipe"
})
export class MyPipe implements PipeTransform {
  constructor(private readonly myService: MyService) {}

  transform(value: Item): boolean {
    // stuff with your service
    return true;
  }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接