用笑话和打字稿为交集观察员嘲笑

时间:2020-08-31 05:42:31

标签: typescript mocking jestjs react-testing-library intersection-observer

如果我们在开玩笑中使用带有打字机的笑话,并且使用了交叉观察器,那么交叉观察器的模拟将变得很困难。到目前为止,我在:


beforeEach(() => {
  // IntersectionObserver isn't available in test environment
  const mockIntersectionObserver = class {
    observe() {
      console.log(this);
    }

    unobserve() {
      console.log(this);
    }

    disconnect() {
      console.log(this);
    }

    root = null

    rootMargin = '0'

    thresholds=[1]

    takeRecords=() => ([{
      isIntersecting: true,
      boundingClientRect: true,
      intersectionRatio: true,
      intersectionRect: true,
      rootBounds: true,
      target: true,
       time: true,
    }])
  };
  window.IntersectionObserver = mockIntersectionObserver;
});

但是这仍然会引发错误,例如:

Type 'typeof mockIntersectionObserver' is not assignable to type '{ new (callback: IntersectionObserverCallback, options?: IntersectionObserverInit | undefined): IntersectionObserver; prototype: IntersectionObserver; }'.
  The types returned by 'prototype.takeRecords()' are incompatible between these types.
    Type '{ isIntersecting: boolean; boundingClientRect: boolean; intersectionRatio: boolean; intersectionRect: boolean; rootBounds: boolean; target: boolean; time: boolean; }[]' is not assignable to type 'IntersectionObserverEntry[]'.
      Type '{ isIntersecting: boolean; boundingClientRect: boolean; intersectionRatio: boolean; intersectionRect: boolean; rootBounds: boolean; target: boolean; time: boolean; }' is not assignable to type 'IntersectionObserverEntry'.
        Types of property 'boundingClientRect' are incompatible.
          Type 'boolean' is not assignable to type 'DOMRectReadOnly'.ts(2322

我可以继续为每个元素添加正确的类型,但是有更好的方法吗?

如何将交集观察者添加到开玩笑的环境中?我认为这比像这样嘲笑要好。

2 个答案:

答案 0 :(得分:2)

通常,该模拟应严格遵循IntersectionObserverIntersectionObserverEntry规范,但是可以根据用途将其简化。

如果正确的键入使事情变得更加复杂,则可能不必在模拟中保留类型安全。在这种情况下,类型错误会显示模拟错误。从<a>参考中可以看出,只有IntersectionObserverEntry属性应该是布尔值,而isIntersecting应该是对象,因此为其余部分提供布尔值并忽略类型问题可能会导致在模拟实现中无意间无法正常工作。

使用常规类进行模拟是不切实际的,因为它缺乏Jest间谍提供的功能,例如由框架控制的调用断言和模拟实现。

一个简单的实现是:

boundingClientRect

一个缺点是,当无法直接访问实例或需要在实例化后立即完成操作时,无法更改模拟类成员的实现。这需要在需要时替换整个类的实现。

由于这个原因,让Jest监视具有可在实例化之前访问的原型链的类是有益的。可以利用Jest自动嘲笑来实现此目的,这允许为只读属性定义window.IntersectionObserver = jest.fn(() => ({ takeRecords: jest.fn(), ... })); 访问器,这些只读属性可以像其他任何Jest间谍一样更改实现:

get

这导致生成模拟类的实现,该实现是Jest间谍原型,其方法为无操作间谍。 class IntersectionObserverStub { get root() {} // read-only property takeRecords() { /* implementation is ignored */ } // stub method observe() {} ... } jest.doMock('intersection-observer-mock', () => IntersectionObserverStub, { virtual: true }); window.IntersectionObserver = jest.requireMock('intersection-observer-mock'); jest.spyOn(IntersectionObserver.prototype, 'root', 'get').mockReturnValue(null); // jest.spyOn(IntersectionObserver.prototype, 'takeRecords').mockImplementation(() => ({...})); 访问器保持原样,但是由于它们存在,因此以后可以使用get对其进行模拟。类似spyOn(..., 'get')的方法(可能特定于实例)可以保留而不使用默认实现,并且可以就地模拟,它返回takeRecords时,如果意外调用它,则可能产生比随机预定义值更清晰的错误输出

undefined

答案 1 :(得分:0)

这对我们有用

/* eslint-disable class-methods-use-this */

export default class {
  readonly root: Element | null;

  readonly rootMargin: string;

  readonly thresholds: ReadonlyArray<number>;

  constructor() {
    this.root = null;
    this.rootMargin = '';
    this.thresholds = [];
  }

  disconnect() {}

  observe() {}

  takeRecords(): IntersectionObserverEntry[] {
    return [];
  }

  unobserve() {}
}

然后安装并执行

import MockIntersectionObserver from './MockIntersectionObserver';
window.IntersectionObserver = MockIntersectionObserver;