【问题标题】:reset typescript singleton instance in each unit test在每个单元测试中重置打字稿单例实例
【发布时间】:2020-12-31 02:47:26
【问题描述】:

我有一个像这样的打字稿单例类:

export default class MySingleton {
private constructor({
    prop1,
    prop2,
    ...
  }: MySingletonConfig) {
    
    this.prop1 = prop1 ?? 'defaultProp1';
    this.prop2 = prop2;
    this.prop3 = prop3 ?? 'defaultProp3';

    /* ... some instruction ... */

    MySingleton.instance = this;
  }

  static getInstance(params?: Configuration): MySingleton {
    if (!this.instance && !params) {
      throw MySingleton.instantiationError;
    }

    if (!this.instance) {
      new MySingleton(params);

      return this.instance;
    }

    return this.instance;
  }
}

当我想使用 jest 对其进行单元测试时,如下所示:

describe('getInstance()', () => {
    test('it should return the same instance every time', () => {
      const params = {
       /* ... all the params ... */
      };

     
      const mySingleton = MySingleton.getInstance(params);

      expect(MySingleton.getInstance()).toEqual(mySingleton);
    });
    test('it should return the instance with the default value', () => {
      const params = {
       /* ... ONLY THE REQUIRED PARAMS ... */
      };
     
      const mySingleton = MySingleton.getInstance(params);

      expect(mySingleton.prop1).toEqual('defaultProp1');
      expect(mySingleton.prop3).toEqual('defaultProp3');
    });
  });

这是失败的,因为我们在两个测试之间共享同一个实例(作为单例模式工作),因此第二个实例化是无用的。

有没有办法重置/销毁前一个实例化,以便正确检查这些默认值是否与第二个实例化一起正确设置?

【问题讨论】:

  • 等一下。共享相同单例的测试与具有或不具有默认值的单例有什么关系?如果您不更改它们的值,那么它们将从默认值开始。编辑:澄清一下,如果您检查默认值的测试发生在任何其他修改它们的测试之前,那么它们将具有默认值。
  • 你能从测试中访问 MySingleton.instance 字段吗?如果是这样,您可以在每次测试开始时将其设置为 null。 @Taplar 他正在尝试测试 getInstance 函数。但是,如果有实例,则行为会发生变化,因此他无法正确验证这一点。
  • @chingucoding 我明白这一点。我的意思是,如果值没有改变,它们将是默认值。因此,如果 getInstance 方法的描述有一个 beforeAll 调用 getInstance 以创建默认实例,那么“相同实例”测试和“默认值”测试都可以使用相同的东西并且工作。唯一的复杂情况是,如果 jest/jasmine 设置为以随机顺序运行规范。
  • 我对打字稿不是很熟悉。有没有办法使实例“受保护”?这样,您可以编写一个扩展 MySingleton 类的 TestMySingleton 类,它有一个销毁实例的方法?
  • 如果哪些值没有改变?当其中一个测试成功时,设置了 MySingleton 的实例属性,下一次测试不会更改实例,因为您没有清除实例。问题是关于如何重置该实例,那么如何实现 beforeAll

标签: javascript node.js typescript jestjs


【解决方案1】:

我不明白你为什么不能这样做:

MySingleton.instance = null;
const mySingleton = MySingleton.getInstance(params);

【讨论】:

  • 我将实例设为私有,也许这是我的错误?我希望获取实例的唯一方法是使用 getInstance() 方法
  • 您可以将其保密,该方面仅由编译器强制执行,因此您始终可以在测试中执行let tmp: any = MySingleton; tmp.instance = null 之类的操作
  • 在 TS 中执行此操作的方法是 MySingleton['instance'] = null
  • Estus Flask 解决方案对我有用!酷。
【解决方案2】:

理想情况下,istance 属性应该是 really private,但没有人阻止您在类中添加 reset() 方法。 它不是特别整洁,因为它基本上仅用于测试目的,但至少它会更接近单例模式的规范实现。

话虽如此,我会仔细考虑使用单例是否是个好主意。在对代码进行单元测试时,它可能会让人头疼。 基本上,当您尝试测试一些使用单例的代码时,您在此处遇到的相同问题可能会在其他地方出现。

【讨论】:

  • 感谢您的回复!我考虑了重置(/destroy)功能,出于您所说的确切原因,我对实现它持怀疑态度至于在其他单元测试中使用单例,我会简单地模拟它,因为在另一个测试中测试它没有用: ) 为了更深入地了解,我们正在尝试为支付提供商创建一个库,因此配置在第一次实施后不应更改。这就是我使用单例模式的原因
  • 我明白了。好吧,老实说,我不会将它作为一个单例实现,而是让客户端代码负责实例化一次类并根据需要传递它(作为全局变量,或者使用其他一些方法,具体取决于他们的框架使用)。
  • mmmh 我明白了,我想避免让我们使用(因为我们现在是主要用户)。单例使用的想法是在应用程序开始时设置它,以后不必担心它,只需在需要时获取实例。感谢您的回复、评论和洞察! :)
  • 一点也不 :) 无论如何,这也适用于全局/共享变量(无论你想怎么称呼它)。
  • 注意不要创建硬依赖,如果你做类似function pay() { const paymentLib = MyPaymentLib.getIntance(); paymentLib.pay(); }的事情我宁愿注入依赖function pay(paymentLib = window.myPaymentLib) { // or wherever your variable is coming from paymentLib.pay(); }这只是我的两分钱,但当然,我缺乏完整的知识图片。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2023-01-27
  • 1970-01-01
  • 2019-04-05
  • 2012-05-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多