Angular/Karma 中的模拟文档

Phi*_*hil 7 unit-testing karma-runner karma-jasmine angular

如何在 Angular 中模拟 DOCUMENT(HTMLDocument 的影子表示)?实现是在构造函数中使用 this:

@Inject(DOCUMENT) private document: Document
Run Code Online (Sandbox Code Playgroud)

在查看了如何在 Angular 2 服务中注入文档后,我已将其放入我的 .spec 设置中:

const lazyPath = 'dummy';
const pathname = `/${lazyPath}`;
const document = { location: { pathname } as Location } as Document;
beforeEachProviders(() => ([ {provide: DOCUMENT, useValue: document} ]));
Run Code Online (Sandbox Code Playgroud)

但这给了我错误:

ERROR in ./src/app/main/components/app-lazy/app-lazy.component.spec.ts
Module not found: Error: Can't resolve '@angular/core/testing/src/testing_internal' in '...'
resolve '@angular/core/testing/src/testing_internal' in '....'
  Parsed request is a module
  using description file: .../package.json (relative path: ...)
    Field 'browser' doesn't contain a valid alias configuration
    resolve as module
Run Code Online (Sandbox Code Playgroud)

当我在 TestBed.configureTestingModule 中使用简单的提供程序:[]而不是testing_internal包中的beforeEachProviders时,该组件未定义,例如未正确初始化。当我从注入的文档切换到窗口对象(我无法在其上设置/模拟位置)时,它仅在单元测试中初始化(在非测试执行中都有效)。我能做些什么?

Joe*_*oeO 7

您应该避免模拟整个文档对象并模拟/监视其上的单个方法/属性。

假设您的组件/服务中有以下内容:

import { DOCUMENT } from '@angular/common';
...
constructor(@Inject(DOCUMENT) private document: Document) {}

Run Code Online (Sandbox Code Playgroud)

您可以通过将文档对象注入到您的beforeEach

describe('SomeComponent', () => {
  let component: SomeComponent;
  let doc: Document;

  beforeEach(() => {
    TestBed.configureTestingModule({
      declarations: [SomeComponent],
      imports: [
        RouterTestingModule,
        HttpClientTestingModule
      ]
    });
    const fixture = TestBed.createComponent(AppComponent);
    component = fixture.componentInstance;
    doc = TestBed.inject(DOCUMENT); // Inject here **************
  });


  it('set document title', () => {
    component.setPageTitle('foobar'); // Assuming this component method is `this.document.title = title`
    expect(doc.title).toBe('foobar');
  });

  it('calls querySelectorAll', () => {
    const spy = spyOn(doc, 'querySelectorAll');
    component.someMethodThatQueries();
    expect(spy).toHaveBeenCalled();
  });

});
Run Code Online (Sandbox Code Playgroud)


Sim*_*uer 6

我很可能遇到与@Phil类似的问题。该问题似乎与将 DOCUMENT 注入组件有关。

当您模拟注入的 DOCUMENT 时,在内部调用 时,调用TestBed.createComponent()会引发错误document.querySelectorAll()

TestBed.createComponent()似乎正在访问注入的模拟文档对象。不确定这是一个错误还是有意为之。

我最近遇到了 Angular 11 的问题。因为我懒得建立一个新的 stackblitz,所以我在基于 Angular 8 的现有 stackblitz 上复制了它。但问题是一样的。

https://stackblitz.com/edit/jasmine-in-angular-beomut?file=src%2Fapp%2Fapp.component.spec.ts

我当前针对此问题的解决方案/解决方法是:

将相关逻辑移至document服务中。在那里可以轻松地测试它,而无需调用TestBed.createComponent()。然后,您可以在您的组件中模拟该服务。


DJ *_*use 3

将此作为答案发布,因为格式在评论中不起作用。

如果可能的话,你能分享一下 stackblitz 吗?当我需要注入模拟时,我通常将其设置为:

  // ... beginning of file

  const mockDocument = { location: { pathname } };

  beforeEach(() => TestBed.configureTestingModule({
    imports: [...],
    // Provide DOCUMENT Mock 
    providers: [
      { provide: DOCUMENT, useValue: mockDocument }
    ]
  }));

  // ...rest of file
Run Code Online (Sandbox Code Playgroud)

  • 你确定这应该有效吗?我的应用程序有点复杂,但是将窗口更改为(注入的)文档的部分是导致测试通过或失败的原因(TypeError: el.querySelectorAll 不是函数)。您的解决方案是我最初使用的解决方案,这是我遇到的错误。 (3认同)