【问题标题】:How to mock Angular external Javascript object如何模拟 Angular 外部 Javascript 对象
【发布时间】:2020-07-04 10:18:28
【问题描述】:

我有一个 Angular 应用程序,其唯一目的是构建 @angular/elements(没有 index.html、没有 app.component 等)。

这些元素在 .NET Mvc 应用程序中使用,它提供了两种我试图在单元测试中模拟的东西:

1 - 外部库

例如,JQuery、lodash 等

2 - 客户端框架“foo”

在包含我的 Web 元素的 .NET MVC 视图中,MVC 应用程序创建了一个名为 foo 的对象。该对象不是 ES6 模块。它是用漂亮的 foo = new Foo({ someOptionsGeneratedFromServerSide}); 手动创建的;

这两种情况都在我的网络元素中使用。例如,一个组件将使用函数 foo.displayAlert('With a message')。 我发现使我的角度应用程序遵守的唯一解决方案是添加以下内容:

// tslint:disable-next-line: max-line-length no-reference
/// <reference path='path/to/the/definition.d.ts' />

我知道这很不正统。

在我尝试进行一些单元测试之前,Angular 应用程序运行良好。 注意:它不能单独工作(ng serve 没有预期的用处)。

一个例子

it('should render title', () => {
  const fixture = TestBed.createComponent(AppComponent);
  fixture.detectChanges();
  const compiled = fixture.nativeElement;
  expect(compiled.querySelector('.content span').textContent).toContain('ng-jest app is running!');
});

对于这个组件/钩子:

ngOnInit() {
  foo.displayAlert('With a message');
}

将崩溃并显示以下(非常预期的)消息:

AppComponent › should render title
ReferenceError: foo is not defined

我尝试在测试前显式模拟对象:

  let foo: any;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      imports: [
        RouterTestingModule
      ],
      declarations: [
        AppComponent
      ],
    }).compileComponents();
    foo = {
      displayAlert: Function
    };
  }));

到目前为止没有运气。我在 x.spect.ts 中做了很多关于这个对象的嘲笑尝试,但没有结果。

我尝试使用 index.html 创建另一个 Angular 应用程序,其中包含 MVC 视图等所有资源,但失败了。

结语

对于像 JQuery / lodash 这样的第 3 方,我不希望将它们添加到我的 Angular 项目中,例如: npm i -D lodash 因为我不希望它们包含在我的 vendor.js 文件中。这些库由 .Net Mvc 应用程序提供,这很好。 这也适用于 foo 对象。 最后,我尝试使用 (Jest) 和不使用 (Karma) 无头浏览器进行测试。

任何帮助将不胜感激!

2020 年 3 月 27 日编辑

请救救我!

2020 年 3 月 28 日编辑

尝试了 jasmine.createSpyObject 和 spyOn,也没有运气:(

【问题讨论】:

    标签: angular unit-testing mocking angular-elements angular-jest


    【解决方案1】:

    复杂的设置,你自己也不容易。

    但是,您似乎只需要将 foo 伪装成全局变量,因为全局范围是您的组件期望变量所在的位置。当您在 ECMAScript 模块中声明 let foo 时,您正在创建一个范围为该文件的变量,您的组件无法访问该变量。

    beforeEach(() => {
      window.foo = {
        displayAlert: () => {},
      };
    });
    

    这可能会导致您与 TypeScript 搏斗。快速修复是这样的

    beforeEach(() => {
      (window as any).foo = {
        displayAlert: () => {},
      };
    });
    

    【讨论】:

    • 你完全正确, foo 对象确实在全局范围内,应该在那里定义。至于您的快速修复,您预见到我在第一段代码中遇到的问题:缺少打字稿定义中定义的 foo 对象的属性和方法。我不介意快速修复,因为这只是为了模拟。谢谢!!
    猜你喜欢
    • 1970-01-01
    • 2018-08-24
    • 2018-10-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-11
    • 1970-01-01
    相关资源
    最近更新 更多