Search code examples
laraveljestjsvuejs3laravel-echovitest

Mocking laravel-echo with vitest and @vue/test-utils


I'm working on a Vue3 app in a Laravel/Vue/vite stack and using laravel-echo for pusher stuff. Some components are interacting with Echo.private or Echo.notification and I've thus far been unable to mock Echo to test it or if nothing else to prevent many errors when testing other aspects of the components. Example usage:

Echo.private(`user.${user?.id}`)
      .listenToAll((event, data) => {
        if (data?.data?.message) {
          adminMessagesStore.addMessage(data.data.message);
          scrollToBottom(bottomOfList.value);

          adminMessagesStore.getRecent();
        }
      });

In this case, I am using that code in the "onMounted" hook after fetching initial data.

My first instinct was/is to mock like so:

vi.mock('laravel-echo');

With that, I get the error TypeError: default.private is not a function. I then tried this:

const mockEcho = {
  private: vi.fn().mockReturnThis(),
  listenToAll: vi.fn(),
};
vi.mock('laravel-echo', () => mockEcho);

and get the error Error: [vitest] There was an error when mocking a module. If you are using "vi.mock" factory, make sure there are no top level variables inside, since this call is hoisted to top of the file. Read more: https://vitest.dev/api/vi.html#vi-mock. I've mocked several third party dependencies without such issues (axios, @vueuse/core, my own custom composables or utilities).

Does anyone have experience with successfully mocking and testing laravel-echo with either vitest or jest and @vue/test-utils? The boostrap setup is boilerplate. These errors are occurring even with the simplest mounting tests.


Solution

  • Alright, figured this out like so:

    vi.mock('laravel-echo', async () => {
      const actual = await vi.importActual('laravel-echo');
    
      return {
        ...actual,
        default: {
          private: vi.fn(() => {
            return {
              listenToAll: vi.fn(),
            };
          }),
        },
      };
    });
    

    The mock was not actually mocking private or listenToAll, so had to partially mock and override the default property with expected mocked functions.