【问题标题】:Angular2 unit testing : testing a component's constructorAngular2 单元测试:测试组件的构造函数
【发布时间】:2017-09-15 19:41:28
【问题描述】:

一切尽在标题中:如何测试组件的构造函数中所做的事情?

为了您的信息,我正在使用一个需要设置的服务,我想看看我在构造函数中调用的 2 个方法是否被正确调用。

我的组件的构造函数:

constructor(
  public router: Router,
  private profilService: ProfileService,
  private dragula: DragulaService,
  private alerter: AlertService
) {
  dragula.drag.subscribe((value) => {
    this.onDrag(value);
  });
  dragula.dragend.subscribe((value) => {
    this.onDragend(value);
  });
}

【问题讨论】:

  • 使用测试台注入假的DragulaService 并窥探其方法?
  • 我想,问题是我无法联系到contrsuchor来测试我的存根是否被调用
  • 什么意思“到达构造函数”?当组件被测试台中的 DI 系统实例化时,当您 TestBed.createComponent(YourComponent) 时,它会被调用。
  • 我的意思是,我想测试构造函数中编写的代码是否被触发,通过测试构造函数,就像它是任何其他方法一样
  • 可以通过手动创建 new YourComponent(...) 来调用构造函数,但您应该让 DI 系统完成其工作。这就是该单元在实践中的使用方式,想想它的公共接口,就像其他组件看到的那样。如下所示,听起来您正在编写(或尝试编写)与当前实现密切相关的脆弱测试。

标签: unit-testing angular angular2-testing


【解决方案1】:

我会使用 DI 系统注入一个假服务,这意味着编写测试如下:

describe('your component', () => {
  let fixture: ComponentFixture<YourComponent>;
  let fakeService;
  let dragSubject = new ReplaySubject(1);
  ...

  beforeEach(async(() => {
    fakeService = { 
      drag: dragSubject.asObservable(),
      ... 
    };

    TestBed.configureTestingModule({
      declarations: [YourComponent, ...],
      providers: [
        { provide: DragulaService, useValue: fakeService }, 
        ...
      ],
    });
  }));

  beforeEach(() => {
    fixture = TestBed.createComponent(YourComponent);
    fixture.detectChanges();
  });

  it('should do something when a drag event occurs', () => {
    dragSubject.next({ ... });
    fixture.detectChanges();
    ...
  });
});

这使您可以随时通过在主题上调用.next 来触发“拖动事件”,这会导致伪造服务上的字段的订阅者被调用。然后,您可以就您期望的结果做出断言。

注意,您不需要自己拨打constructor;此方法在 DI 系统实例化您的组件时调用,即在调用 TestBed.createComponent 时。

我建议您不要监视组件方法(例如this.onDrag),只需确保它们被调用,而是测试这些方法应该执行的任何结果发生;这使得测试对特定实现的变化更加稳健(我在我的博客上写了一点:http://blog.jonrshar.pe/2017/Apr/16/async-angular-tests.html)。

【讨论】:

  • 所以你告诉我,我应该调用想要的方法,而不是测试构造函数,看看它们是否被调用? (我是单元测试的新手,我认为您必须测试每个方法,包括构造函数)
  • @tricheriche 不,我是说您通过确保服务上的事件与正确的行为挂钩来测试构造函数的作用。您可以简单地测试构造函数在假服务上的适当间谍字段上调用.subscribe(例如执行类似{ drag: jasmine.createSpyObj('fake drag event', ['subscribe']), ... } 的操作),但这与当前的具体实现密切相关,而不是您正在实现的实际功能。
  • @trichetriche 服务没有在构造函数中调用,只是订阅了。您尝试实现的实际行为,因此您应该测试的是,当拖动事件发生时会发生什么。这就是上述测试检查的内容。 “我的意思是我不应该调用它” - 调用 what? “所以如果有人更改构造函数代码,它会抛出错误” - 如果构造函数发生更改以致不再订阅该服务,并且没有进行任何其他更改以确保 功能 仍然有效,则测试将失败。这就是应该发生的事情。
  • @tricheriche 没问题。其实我最近为一些同事写了一篇关于我们的 Angular 测试的博文,你可能会发现它很有用:blog.jonrshar.pe/2017/Apr/16/async-angular-tests.html
  • 非常感谢您的回答!
【解决方案2】:

在构造函数中测试任何东西的简单方法是创建组件实例,然后对其进行测试。

it('should call initializer function in constructor', () => {
  TestBed.createComponent(HomeComponent); // this is the trigger of constructor method
 expect(sideNavService.initialize).toHaveBeenCalled(); // sample jasmine spy based test case
});

有一点需要注意,如果你想区分构造函数和ngOnInit,那么不要在beforeEach()里面调用fixture.detectChanges()。而是在需要时手动调用。

【讨论】:

  • 谢谢它帮助很大。
【解决方案3】:

由于 OP 声明“我想看看我在构造函数中调用的 2 个方法是否被正确调用。”我有更好的方法。

编写单元测试。您不需要为此使用测试台。它会大大减慢您的测试速度。手动实例化你的模拟。在您感兴趣的方法上设置间谍,然后使用您已实例化的存根手动调用组件构造函数并设置间谍。然后测试是否正确调用了间谍方法。

关键是从原始服务类扩展您的存根。 jasmine.createSpyObj 有助于模拟像 Router 这样的 Angular 类。

【讨论】:

  • 已经有一个公认的答案,它特别适合我。你的方法不是因为它是每个组件的问题,我做过的每一个服务都有一些代码到构造函数中。
  • 您可以对每个组件执行此操作。您真的不会将测试台用于纯单元测试。否则,您的测试需要很长时间才能完成。相信我,我现在正在为此苦苦挣扎。看看这个:chariotsolutions.com/blog/post/…
  • 无意冒犯,但是避免使用可用的抽象虽然它提供了如此多的功能,但它有点愚蠢。例如,当我测试我的服务时,模拟后端非常有用!它需要为此提供测试平台。在 200 多个测试的 14k 行代码中,我的测试时间是 6 秒,我觉得我很好 :D
  • 我被冒犯了。我不是告诉你不要嘲笑老兄。我告诉你如何更干净、更快地模拟它。您当然应该在必要时使用测试台。我只是说你所要求的不需要它。感谢您的反对。
  • 你告诉我要像这样测试我的所有组件。我最初的问题是如何测试构造函数,而不是如何进行所有测试。我几乎总是需要测试台,当我不需要时,我就不使用它。但我不会仅仅因为我会获得 5ms 就排除这一点。我告诉你,你的回答太晚了,超出了主题,不适合我的情况。所以是的,我投反对票,如果你不知道,我也会因此而失分,所以这不是出于快乐!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-10-19
  • 2013-03-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-09-28
  • 1970-01-01
相关资源
最近更新 更多