【问题标题】:How do I unit test unsubscribe in Angular and Jasmine?如何在 Angular 和 Jasmine 中对退订进行单元测试?
【发布时间】:2019-02-21 04:45:12
【问题描述】:

我的组件中有私有类成员,例如

private querySubscription: Subscription;.

在我的ngOnDestroy() 我取消订阅:

ngOnDestroy(): void { this.querySubscription.unsubscribe(); this.userSettingsSubscription.unsubscribe(); }

如何在 jasmine 的单元测试中测试我是否真的正确取消了订阅?我可以以编程方式调用ngOnDestory(),因为它是公开的,但我无法测试我的私人班级成员。

【问题讨论】:

  • 我相信您需要以某种方式模拟/监视您将可观察(或其他)订阅分配给 querySubscriptionuserSettingsSubscription 的位置,然后替换(使用间谍?) unsubscribe 方法知道它何时被调用 - 但这只是一个想法。

标签: angular unit-testing jasmine observable


【解决方案1】:

如果您使用的是 Typescript,我想您在设置 testBed 时要在测试模块中测试私有成员,您必须使用注入器调用您的服务。

beforeEach(() => {
    TestBed.configureTestingModule({
        providers: [
            YourService,
        ],
        imports: [ HttpClientTestingModule ],
    });
    const injector = getTestBed();
    service = injector.get(YourService);
}); 

现在您可以使用service.myMethod 访问您的服务公共方法,使用(service as any).privateMethod() 访问您的私有方法。

由于 typescript 始终编译为 JavaScript,而在 JavaScript 中,您没有公共私有成员,因此您必须使用 (service as any) 构造。

编辑 1

在你能够访问一个私有之后,你可以访问它的属性,所以你接下来要做的是(在伪代码中):

    spyOn((service as any).privateMethod, 'setBar');

    service.ngOnDestroy()

    expect((service as any).privateMethod.setBar).toHaveBeenCalled();

【讨论】:

  • 虽然最后一部分是对的,但你的回答并不能解决他的问题。
  • 好吧,我向他展示了如何从测试中访问私人成员。如果你知道如何访问一个私有成员,那么你可以存根它、监视它或做任何你想做的事情。在我们的单元测试中,我们毫无问题地做到了。使用 sinon,你可以做一些简单的事情,比如 const privateTest = spy((service as any).querySubscription, "unsubscribe")。然后断言它是否已被调用。有了 Jasmine,我相信你可以做类似的事情。
  • 是的,但是这个使用注入器获取依赖实例的概念可以在单元测试指南的开头找到。您没有使其适应他的需求,您也没有回答他应该如何嘲笑它。我只是通用代码。
  • 此外,即使您可以使用它来获取依赖关系,它也高度依赖于您的测试实现以及您是否使用了测试台。因为他的问题中没有说明,所以你无法知道这个答案是否适合他的情况。
  • 他没有提到使用TestBed,它通常用于像Jasmine这样的Angular测试。在我们的单元测试中,我们不使用任何这些,我们使用注入器和 Sinon + Chai,并且此实现适用于两者。你建议他将他的财产改为公共财产,而我同意你关于 TDD 的观点,你的解决方案也不合适。
【解决方案2】:

您应该模拟您的私有变量(您可以通过数组表示法访问),然后调用您的函数,并期望订阅被关闭。

Stackblitz

  it('should unsubscribe on destroy', () => {
    component['querySubscription'] = of(true).subscribe();

    component.ngOnDestroy();

    expect(component['querySubscription'].closed).toBeTruthy();
  });

编辑 1

由于数组表示法,此解决方案有效。在 javascript 中,您没有私有或公共变量:您只有变量。

Typescript 强制您遵守私有/公共,但您始终可以解决这个问题。

如果你不想这样做,你应该创建一个公共函数来创建这样的订阅

createSubscription() {
  this.querySubscription = this.someFunction().subscribe();
}

如果您不想使用数组表示法,这是唯一可行的解​​决方案(我忘了说,这根本不是一个坏习惯)。

由你来选择你喜欢哪一个!

【讨论】:

  • 这似乎可行,但是当我们将其更改为 component.querySubscription = of(true).subscribe(); 时,编译器会抱怨。如果这个解决方案是正确的,我会很感兴趣 - 也许其他人对此有意见?
  • @Buczkowski 我已对我的答案进行了编辑,请随时查看!
  • 我不想公开变量,只是为了测试。在我看来,这是不好的做法,原因有几个。我也不想使用 workaroung 访问私有变量,因为 typescript 给我这些是有原因的。目前,我倾向于进行隐式测试。
  • 我没有告诉你公开你的变量。数组表示法不是一种解决方法,它是用于动态获取对象属性的完全有效的语法。私有变量应该已经是实现细节。如果您对任何解决方案都不满意,那么您就有了设计缺陷。您应该尝试进行测试驱动开发以避免这种情况。最后,Typescript 为开发人员提供私有和公共属性是为了方便智能感知,而不是作为设计模式(因为如上所述,JS 没有私有或公共属性)。
  • 如果您不想使用数组表示法,您将不得不这样做。而且我个人认为您不应该只测试可以从组件外部访问的内容。按照这种逻辑,您可能会在依赖中错过很多副作用(但这只是我的观点,而不是事实)。所有解决方案都是“可行的”,但似乎它们确实并不“适合你”。而且我没有说 Typescript 是更好的智能感知,我说变量访问器是 Typescript 中智能感知的便利。这是真的(这次不是我的观点)。但是祝你的解决方案好运,让我也发布!
【解决方案3】:

可能是这样的:

export class ObservableHelper {
    getMockObservable() {
        const s = new Subscription();
        const obs = new Observable();
        spyOn(obs, 'subscribe').and.returnValue(s);
        spyOn(s, 'unsubscribe');
        return {obs, s};
    }
}

在您的规范文件中:

let helper = new ObservableHelper();
let o1 = helper.getMockObservable();

其中一条评论已经提到,在创建新订阅时必须进行 Mock。如果您正在调用一个外部服务并返回一个 observable,则 o1.obs 可以用作相同的返回值。

例如如果您正在订阅 PubSub 服务:

pubSubSpy.sub.withArgs(jasmine.any(Object), 'test').and.returnValue(o1.obs);


// Act
service.ngOnDestroy();

// Assert
expect(o1.s.unsubscribe).toHaveBeenCalledTimes(1);

【讨论】:

    【解决方案4】:

    在我的 beforeEach 中,我将 Subscription 设置为 rxjs 类的新实例,然后我可以直接监视它。

    import { Subject, Subscription } from 'rxjs';
    ....
    let pagingSubscriptionSpy: jasmine.Spy;
    beforeEach(() => {
     service.pagingSubscription = new Subscription();
     pagingSubscriptionSpy = spyOn(service.pagingSubscription, 'unsubscribe');
    })
    ....
    it('calls getEventunsubscribesPaged', () => {
       expect(pagingSubscriptionSpy).toHaveBeenCalledTimes(1);
    });
    
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-09-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多