简体   繁体   中英

How to test a function that uses Nitro's useStorage

I have created a function that allows to manage caching. I use this function to cache the responses to API calls.

export const cache = async (key: string, callback: Function) => {
    const cacheKey = `cache:${key}`;

    const data = await useStorage().getItem(cacheKey);
    if (data) {
        console.log('Get cached data for key: %s', key);
        return data;
    }

    const result = await callback();

    console.log('Caching data for key: %s', key);
    await useStorage().setItem(cacheKey, result);

    return result;
}

Here is a use case:

import { cache } from '~/server/lib/caching/cache-manager';

export default defineEventHandler(async (event) => {
    const config = useRuntimeConfig();

    const domain = event.context.params.domain;
    const id = event.context.params.id;

    const url = `${config.baseUrl}/api/${domain}/${id}`;

    return await cache(url, () => {
        return $fetch(url);
    });
})

I would like to test the 'cache' function with vitest. For info, I added a vitest plugin to manage Nuxt aliases and auto-import, based on https://github.com/nuxt/framework/discussions/5379#discussioncomment-4224823

Here is the test, which does nothing, it just calls the 'cache' function:

import { describe, beforeEach, afterEach, test, expect, vi } from 'vitest'
import { cache } from '~/server/lib/caching/cache-manager'

describe('My test', () => {
    test('my test', () => {
        const result1 = cache('mykey', () => 3);
        const result2 = cache('mykey', () => 3);
    })
})

But I get an error when I call the cache function:

ReferenceError: useStorage is not defined

The Nitro's useStorage is not recognized.

I think the problem is related to #imports which does not include server auto-imports.

I tested the following workaround but it still doesn't work:
https://github.com/nuxt/framework/issues/4290

You can test here:
https://stackblitz.com/edit/nuxt-starter-e3vtf2?file=tests%2Fserver%2Flib%2Fcaching%2Fcache-manager.test.ts

How can I test my 'cache' function that uses 'useStorage'?

I created a storage.ts file which contains the useStorage mock.

import { vi } from "vitest";

const mockedStorage = {
    getItem: vi.fn(),
    setItem: vi.fn()
}

export const useStorage = vi.fn(() => mockedStorage)

I created an index.ts file that exports the mock.

export { useStorage } from './storage'

I declared the index.ts file in the vitest configuration in order to auto import the declared modules:

...
Vue(),
autoImport.vite({
    presets: [
        {
            package: srcDir + '/__mocks__/index.ts'
        },
        {
            package: buildDir + '/imports.d.ts'
        }
    ]
}),
...

If I need to auto import other mocks, I add them in index.ts .

And I updated my test to test my cache function:

import { describe, beforeEach, afterEach, test, expect, vi } from 'vitest';
import { cache } from '~/server/lib/caching/cache-manager';
import { useStorage } from '~/__mocks__/storage';

describe('My test', () => {
  test('my test', async () => {
    useStorage().getItem.mockReturnValue(3);

    const result = await cache(`myKey`, () => 3);
    expect(useStorage().getItem).toBeCalledWith(`cache:myKey`);
    expect(result).toBe(3);
    expect(useStorage().setItem).not.toBeCalled();
  });

  test('my test 2', async () => {
    useStorage().getItem.mockReturnValue(null);

    const result = await cache(`myKey`, () => 3);
    expect(useStorage().getItem).toBeCalledWith(`cache:myKey`);
    expect(result).toBe(3);
    expect(useStorage().setItem).toBeCalledWith(`cache:myKey`, 3);
  });
});

You can test here:

https://stackblitz.com/edit/nuxt-starter-tl2ecc?file=tests/server/lib/caching/cache-manager.test.ts

I would suggest proxying the function in its own module and mocking it, at least until Nuxt has a reliable solution.

For example, create a module that exports the proxied method:

// server/lib/caching/use-storage.ts

const proxyUseStorage = () => useStorage()

export { proxyUseStorage as useStorage }

In the same directory, create a __mocks__/use-storage.ts file and mock how you see fit – for this example I'm simply re-instantiating the default in-memory storage interface:

// server/lib/caching/__mocks__/use-storage.ts

import { createStorage } from 'unstorage'
import { vi } from 'vitest'

export const useStorage = vi.fn(() => createStorage())

Explicitly import the method where it's used because you want to make sure it can later be mocked in tests:

// server/lib/caching/cache-manager.ts

import { useStorage } from './use-storage'

export const cache = async (key: string, callback: Function) => {
  const cacheKey = `cache:${key}`
  const data = await useStorage().getItem(cacheKey)
  // ...
}

Finally, mock the module before your tests (ensuring the path used is relative to the test file):

// server/lib/caching/cache-manager.spec.ts

import { expect, test, vi } from 'vitest'
import { cache } from '~/server/lib/caching/cache-manager'

vi.mock('./use-storage')

test('cache', async () => {
  expect(await cache('myKey', () => 3)).toBe(3)
})

YMMV depending on your setup, but this should give you a foundation to get mocking.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM