JS - Testing code that uses an IntersectionObserver

独自空忆成欢 提交于 2020-07-15 06:32:11

问题


I have a (rather poorly written) javascript component in my application that handles infinite scroll pagination, and i'm trying to rewrite it to use the IntersectionObserver, as described here, however i'm having issues in testing it.

Is there a way to drive the behavior of the observer in a QUnit test, i.e. to trigger the observer callback with some entries described in my tests?

A possible solution I have come up with is to expose the callback function in the component's prototype and to invoke it directly in my test, something like this:

InfiniteScroll.prototype.observerCallback = function(entries) {
    //handle the infinite scroll
}

InfiniteScroll.prototype.initObserver = function() {
    var io = new IntersectionObserver(this.observerCallback);
    io.observe(someElements);
}

//In my test
var component = new InfiniteScroll();
component.observerCallback(someEntries);
//Do some assertions about the state after the callback has been executed

I don't really like this approach since it's exposing the fact that the component uses an IntersectionObserver internally, which is an implementation detail that in my opinion should not be visible to client code, so is there any better way to test this?

Bonus love for solutions not using jQuery :)


回答1:


Here's another alternative based on previous answers, you can run it inside the beforeEach methods, or at the beginning of the .test.js file.

You could also pass parameters to the setupIntersectionObserverMock to mock the observe and/or unobserve methods to spy on them with a jest.fn() mock function.

/**
 * Utility function that mocks the `IntersectionObserver` API. Necessary for components that rely
 * on it, otherwise the tests will crash. Recommended to execute inside `beforeEach`.
 * @param {object} intersectionObserverMock - Parameter that is sent to the `Object.defineProperty`
 *      overwrite method. `jest.fn()` mock functions can be passed here if the goal is to not only
 *      mock the intersection observer, but its methods.
 */
export function setupIntersectionObserverMock({
  observe = () => null,
  unobserve = () => null,
} = {}) {
  class IntersectionObserver {
    observe = observe;
    unobserve = unobserve;
  }
  Object.defineProperty(
    window,
    'IntersectionObserver',
    { writable: true, configurable: true, value: IntersectionObserver }
  );
  Object.defineProperty(
    global,
    'IntersectionObserver',
    { writable: true, configurable: true, value: IntersectionObserver }
  );
}



回答2:


In your jest.setup.js file, mock the IntersectionObserver with the following implementation:

global.IntersectionObserver = class IntersectionObserver {
  constructor() {}

  disconnect() {
    return null;
  }

  observe() {
    return null;
  }

  takeRecords() {
    return null;
  }

  unobserve() {
    return null;
  }
};

Instead of using the Jest Setup File, you can do this mocking also directly in your tests or in your beforeAll,beforeEach blocks.




回答3:


Same problem in 2019 this is how I solved it:

import ....

describe('IntersectionObserverMokTest', () => {
  ...
  const observeMock = {
    observe: () => null,
    disconnect: () => null // maybe not needed
  };

  beforeEach(async(() => {
    (<any> window).IntersectionObserver = () => observeMock;

    ....
  }));


  if(' should run the Test without throwing an error for the IntersectionObserver', () => {
    ...
  })
});

So I create a mock object, with the observe (and disconnect) method and overwrite the IntersectionObserver on the window object. Depending on your usage, you might have to overwrite other functions (see: https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API#Browser_compatibility )

The code is inspired by https://gist.github.com/ianmcnally/4b68c56900a20840b6ca840e2403771c but doesn't use jest




回答4:


None of the posted answered worked for me because of our configuration of TypeScript and React (tsx) we're using. Here's what finally worked:

beforeEach(() => {
  // IntersectionObserver isn't available in test environment
  const mockIntersectionObserver = jest.fn();
  mockIntersectionObserver.mockReturnValue({
    observe: () => null,
    unobserve: () => null,
    disconnect: () => null
  });
  window.IntersectionObserver = mockIntersectionObserver;
});



回答5:


I had this problem with a setup based on vue-cli. I ended up using a mix of the answers I saw above:

   const mockIntersectionObserver = class {
    constructor() {}
    observe() {}
    unobserve() {}
    disconnect() {}
  };

  beforeEach(() => {
    window.IntersectionObserver = mockIntersectionObserver;
  });


来源:https://stackoverflow.com/questions/44249985/js-testing-code-that-uses-an-intersectionobserver

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!