N
Nuxt3w ago
Yuri

Testing $fetch

My api.ts utils:
export const apiBack = async <T>(
url: string,
token: string,
options: NitroFetchOptions<string> = {},
): Promise<T> => {
options.baseURL = process.env.BITRIX_API_URL
setAuthorizationHeader(options, token)

const response = await $fetch<T>(url, options)
return response as T
}
export const apiBack = async <T>(
url: string,
token: string,
options: NitroFetchOptions<string> = {},
): Promise<T> => {
options.baseURL = process.env.BITRIX_API_URL
setAuthorizationHeader(options, token)

const response = await $fetch<T>(url, options)
return response as T
}
I write this test:
import { describe, it, expect,} from 'vitest';
import {apiBack} from "~/utils/api";
import {testMockTokenAccess, testMockUrlRelative} from "~/utils/tests";
import { $fetch } from '@nuxt/test-utils'


describe('api Utils',()=>{
it('Проверка корректной работы запросов к беку apiBack',()=>{
apiBack(testMockUrlRelative,testMockTokenAccess)
expect($fetch).toHaveBeenCalled();
})
})
import { describe, it, expect,} from 'vitest';
import {apiBack} from "~/utils/api";
import {testMockTokenAccess, testMockUrlRelative} from "~/utils/tests";
import { $fetch } from '@nuxt/test-utils'


describe('api Utils',()=>{
it('Проверка корректной работы запросов к беку apiBack',()=>{
apiBack(testMockUrlRelative,testMockTokenAccess)
expect($fetch).toHaveBeenCalled();
})
})
And I get the error:
FetchError: [GET] "/catalog": 404 Cannot find any path matching /catalog.
FetchError: [GET] "/catalog": 404 Cannot find any path matching /catalog.
on this line
const response = await $fetch<T>(url, options)
const response = await $fetch<T>(url, options)
What is correct variant to check my apiBack function ?
11 Replies
kapa.ai
kapa.ai3w ago
Hi @Yuri. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave: ---------------------- To properly test your apiBack function, you need to mock the $fetch function and set up the test environment correctly. The error you're seeing suggests that the test is actually trying to make a real network request, which is not what we want in a unit test. Here's a correct approach to test your apiBack function: 1. First, you need to mock the $fetch function. In Nuxt 3, you can use the vi.mock function from Vitest to mock the $fetch utility. 2. Then, you can set up expectations for how the mock should be called and what it should return. Here's a corrected version of your test:
import { describe, it, expect, vi } from 'vitest';
import { apiBack } from "~/utils/api";
import { testMockTokenAccess, testMockUrlRelative } from "~/utils/tests";

// Mock the $fetch function
vi.mock('#app', () => ({
$fetch: vi.fn()
}))

describe('api Utils', () => {
it('Проверка корректной работы запросов к беку apiBack', async () => {
// Setup mock response
const mockResponse = { data: 'test data' };
const $fetch = vi.mocked(globalThis.$fetch);
$fetch.mockResolvedValue(mockResponse);

// Call the function
const result = await apiBack(testMockUrlRelative, testMockTokenAccess);

// Check if $fetch was called with correct parameters
expect($fetch).toHaveBeenCalledWith(testMockUrlRelative, {
baseURL: process.env.BITRIX_API_URL,
headers: {
Authorization: `Bearer ${testMockTokenAccess}`
}
});

// Check if the function returns the mock response
expect(result).toEqual(mockResponse);
});
});
import { describe, it, expect, vi } from 'vitest';
import { apiBack } from "~/utils/api";
import { testMockTokenAccess, testMockUrlRelative } from "~/utils/tests";

// Mock the $fetch function
vi.mock('#app', () => ({
$fetch: vi.fn()
}))

describe('api Utils', () => {
it('Проверка корректной работы запросов к беку apiBack', async () => {
// Setup mock response
const mockResponse = { data: 'test data' };
const $fetch = vi.mocked(globalThis.$fetch);
$fetch.mockResolvedValue(mockResponse);

// Call the function
const result = await apiBack(testMockUrlRelative, testMockTokenAccess);

// Check if $fetch was called with correct parameters
expect($fetch).toHaveBeenCalledWith(testMockUrlRelative, {
baseURL: process.env.BITRIX_API_URL,
headers: {
Authorization: `Bearer ${testMockTokenAccess}`
}
});

// Check if the function returns the mock response
expect(result).toEqual(mockResponse);
});
});
This approach addresses several issues: 1. It properly mocks the $fetch function, preventing actual network requests. 2. It sets up expectations for how $fetch should be called and what it should return. 3. It checks if the apiBack function correctly passes the parameters to $fetch. 4. It verifies that apiBack returns the expected result. Note that this test assumes that your setAuthorizationHeader function sets the Authorization header with a Bearer token. If that's not the case, you may need to adjust the expectation accordingly. Also, make sure that your tsconfig.json or vite.config.ts is set up correctly to resolve the #app import, which is how Nuxt 3 exposes its runtime utilities for testing. For more information on testing in Nuxt 3, you can refer to the Nuxt 3 Testing documentation (https://nuxt.com/docs/getting-started/testing). ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai3w ago
<@740173469404364872> Kapa.ai is still learning and improving, please let me know how I did by reacting below.
Yuri
YuriOP3w ago
@kapa.ai I get a new error:
TypeError: $fetch.mockResolvedValue is not a function
TypeError: $fetch.mockResolvedValue is not a function
kapa.ai
kapa.ai3w ago
Let me check the knowledge base for you again. One moment! :books: ---------------------- I apologize for the confusion in my previous answer. The error you're encountering suggests that the mocking approach needs to be adjusted. Let's correct this using the proper Nuxt 3 testing utilities. In Nuxt 3, we should use mockNuxtImport to mock the $fetch function. Here's the corrected version of the test:
import { describe, it, expect, vi } from 'vitest';
import { apiBack } from "~/utils/api";
import { testMockTokenAccess, testMockUrlRelative } from "~/utils/tests";
import { mockNuxtImport } from '@nuxt/test-utils/runtime';

describe('api Utils', () => {
it('Проверка корректной работы запросов к беку apiBack', async () => {
// Setup mock response
const mockResponse = { data: 'test data' };
const mockFetch = vi.fn().mockResolvedValue(mockResponse);

// Mock the $fetch function
mockNuxtImport('$fetch', () => mockFetch);

// Call the function
const result = await apiBack(testMockUrlRelative, testMockTokenAccess);

// Check if $fetch was called with correct parameters
expect(mockFetch).toHaveBeenCalledWith(testMockUrlRelative, {
baseURL: process.env.BITRIX_API_URL,
headers: {
Authorization: `Bearer ${testMockTokenAccess}`
}
});

// Check if the function returns the mock response
expect(result).toEqual(mockResponse);
});
});
import { describe, it, expect, vi } from 'vitest';
import { apiBack } from "~/utils/api";
import { testMockTokenAccess, testMockUrlRelative } from "~/utils/tests";
import { mockNuxtImport } from '@nuxt/test-utils/runtime';

describe('api Utils', () => {
it('Проверка корректной работы запросов к беку apiBack', async () => {
// Setup mock response
const mockResponse = { data: 'test data' };
const mockFetch = vi.fn().mockResolvedValue(mockResponse);

// Mock the $fetch function
mockNuxtImport('$fetch', () => mockFetch);

// Call the function
const result = await apiBack(testMockUrlRelative, testMockTokenAccess);

// Check if $fetch was called with correct parameters
expect(mockFetch).toHaveBeenCalledWith(testMockUrlRelative, {
baseURL: process.env.BITRIX_API_URL,
headers: {
Authorization: `Bearer ${testMockTokenAccess}`
}
});

// Check if the function returns the mock response
expect(result).toEqual(mockResponse);
});
});
This approach uses mockNuxtImport from @nuxt/test-utils/runtime to mock the $fetch function, which is the correct way to mock Nuxt's auto-imports in tests. Key points: 1. We import mockNuxtImport from @nuxt/test-utils/runtime. 2. We create a mock function using vi.fn() and set its resolved value. 3. We use mockNuxtImport to mock the $fetch function with our mock function. 4. The rest of the test remains similar, but we use mockFetch for our expectations. This approach should resolve the TypeError you encountered and allow you to properly test your apiBack function. For more information on mocking Nuxt imports in tests, you can refer to the Nuxt Testing documentation (https://nuxt.com/docs/getting-started/testing#mocknuxtimport). Docs > Getting-started > Testing > Unit Testing > 🛠️ Helpers > mockNuxtImport (https://nuxt.com/docs/getting-started/testing#mocknuxtimport) ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai3w ago
<@740173469404364872> Kapa.ai is still learning and improving, please let me know how I did by reacting below.
Yuri
YuriOP3w ago
@kapa.ai now I get a new error:
Error: Cannot find import "$fetch" to mock
Plugin: nuxt:vitest:mock-transform
Error: Cannot find import "$fetch" to mock
Plugin: nuxt:vitest:mock-transform
kapa.ai
kapa.ai3w ago
Let me check the knowledge base for you again. One moment! :books: ---------------------- I apologize for the confusion. The error you're encountering suggests that the $fetch function is not being recognized as a Nuxt auto-import in your test environment. Let's try a different approach that should work better with Nuxt 3's testing setup. Instead of mocking $fetch directly, we can mock the entire useNuxtApp() return value, which includes the $fetch function. Here's how you can modify your test:
import { describe, it, expect, vi } from 'vitest';
import { apiBack } from "~/utils/api";
import { testMockTokenAccess, testMockUrlRelative } from "~/utils/tests";
import { mockNuxtImport } from '@nuxt/test-utils/runtime';

describe('api Utils', () => {
it('Проверка корректной работы запросов к беку apiBack', async () => {
// Setup mock response
const mockResponse = { data: 'test data' };
const mockFetch = vi.fn().mockResolvedValue(mockResponse);

// Mock the useNuxtApp function
mockNuxtImport('useNuxtApp', () => () => ({
$fetch: mockFetch
}));

// Set the BITRIX_API_URL environment variable for the test
process.env.BITRIX_API_URL = '<https://api.example.com';>

// Call the function
const result = await apiBack(testMockUrlRelative, testMockTokenAccess);

// Check if $fetch was called with correct parameters
expect(mockFetch).toHaveBeenCalledWith(testMockUrlRelative, {
baseURL: '<https://api.example.com',>
headers: {
Authorization: `Bearer ${testMockTokenAccess}`
}
});

// Check if the function returns the mock response
expect(result).toEqual(mockResponse);
});
});
import { describe, it, expect, vi } from 'vitest';
import { apiBack } from "~/utils/api";
import { testMockTokenAccess, testMockUrlRelative } from "~/utils/tests";
import { mockNuxtImport } from '@nuxt/test-utils/runtime';

describe('api Utils', () => {
it('Проверка корректной работы запросов к беку apiBack', async () => {
// Setup mock response
const mockResponse = { data: 'test data' };
const mockFetch = vi.fn().mockResolvedValue(mockResponse);

// Mock the useNuxtApp function
mockNuxtImport('useNuxtApp', () => () => ({
$fetch: mockFetch
}));

// Set the BITRIX_API_URL environment variable for the test
process.env.BITRIX_API_URL = '<https://api.example.com';>

// Call the function
const result = await apiBack(testMockUrlRelative, testMockTokenAccess);

// Check if $fetch was called with correct parameters
expect(mockFetch).toHaveBeenCalledWith(testMockUrlRelative, {
baseURL: '<https://api.example.com',>
headers: {
Authorization: `Bearer ${testMockTokenAccess}`
}
});

// Check if the function returns the mock response
expect(result).toEqual(mockResponse);
});
});
This approach mocks the useNuxtApp() function, which is used internally by Nuxt to provide access to the $fetch function. By mocking useNuxtApp(), we can provide our own mock implementation of $fetch. Key changes: 1. We're now mocking useNuxtApp instead of $fetch directly. 2. We're setting the BITRIX_API_URL environment variable in the test to ensure it's available. 3. The rest of the test remains similar, using mockFetch for our expectations. This approach should resolve the "Cannot find import "$fetch" to mock" error and allow you to properly test your apiBack function. If you're still encountering issues, make sure that your vitest.config.ts (or vitest.config.js) file is properly configured to work with Nuxt 3. You might need to add the following configuration:
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import { resolve } from 'path';

export default defineConfig({
plugins: [vue()],
test: {
globals: true,
environment: 'jsdom',
},
resolve: {
alias: {
'~': resolve(__dirname, './'),
},
},
});
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import { resolve } from 'path';

export default defineConfig({
plugins: [vue()],
test: {
globals: true,
environment: 'jsdom',
},
resolve: {
alias: {
'~': resolve(__dirname, './'),
},
},
});
This configuration ensures that Vitest can properly resolve Nuxt-specific imports and aliases. Docs > Getting-started > Testing > Unit Testing > 🛠️ Helpers > mockNuxtImport (https://nuxt.com/docs/getting-started/testing#mocknuxtimport) provides more information on mocking Nuxt imports in tests. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai3w ago
<@740173469404364872> Kapa.ai is still learning and improving, please let me know how I did by reacting below.
Yuri
YuriOP3w ago
@kapa.ai Error: Test timed out in 5000ms. If this is a long-running test, pass a timeout value as the last argument or configure it globally with "testTimeout".
kapa.ai
kapa.ai3w ago
Let me check the knowledge base for you again. One moment! :books: ---------------------- This error suggests that your test is taking longer than the default timeout to complete. In Vitest, the default timeout for tests is 5000ms (5 seconds). There are a few ways to address this issue: 1. Increase the timeout for this specific test: You can increase the timeout for this specific test by passing a timeout value as the last argument to the it function. Here's how you can modify your test:
import { describe, it, expect, vi } from 'vitest';
import { apiBack } from "~/utils/api";
import { testMockTokenAccess, testMockUrlRelative } from "~/utils/tests";
import { mockNuxtImport } from '@nuxt/test-utils/runtime';

describe('api Utils', () => {
it('Проверка корректной работы запросов к беку apiBack', async () => {
// ... (rest of your test code)
}, 10000); // Increase timeout to 10 seconds
});
import { describe, it, expect, vi } from 'vitest';
import { apiBack } from "~/utils/api";
import { testMockTokenAccess, testMockUrlRelative } from "~/utils/tests";
import { mockNuxtImport } from '@nuxt/test-utils/runtime';

describe('api Utils', () => {
it('Проверка корректной работы запросов к беку apiBack', async () => {
// ... (rest of your test code)
}, 10000); // Increase timeout to 10 seconds
});
2. Increase the global timeout: If you find that many of your tests are taking longer than 5 seconds, you might want to increase the global timeout. You can do this in your Vitest configuration file (vitest.config.ts or vitest.config.js):
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';

export default defineConfig({
test: {
globals: true,
environment: 'jsdom',
testTimeout: 10000, // Set global timeout to 10 seconds
},
// ... (rest of your config)
});
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';

export default defineConfig({
test: {
globals: true,
environment: 'jsdom',
testTimeout: 10000, // Set global timeout to 10 seconds
},
// ... (rest of your config)
});
3. Investigate why the test is taking so long: If your test is consistently taking longer than 5 seconds, it might be worth investigating why. Some possible reasons could be: - Asynchronous operations that are not properly awaited - Slow mock implementations - Complex setup or teardown operations Make sure all your asynchronous operations are properly awaited, and consider using vi.useFakeTimers() if you're dealing with timeouts or intervals in your code. Here's an example of how you might modify your test to use fake timers:
import { describe, it, expect, vi } from 'vitest';
import { apiBack } from "~/utils/api";
import { testMockTokenAccess, testMockUrlRelative } from "~/utils/tests";
import { mockNuxtImport } from '@nuxt/test-utils/runtime';

describe('api Utils', () => {
beforeEach(() => {
vi.useFakeTimers();
});

afterEach(() => {
vi.useRealTimers();
});

it('Проверка корректной работы запросов к беку apiBack', async () => {
// ... (rest of your test code)

await vi.runAllTimersAsync();
});
});
import { describe, it, expect, vi } from 'vitest';
import { apiBack } from "~/utils/api";
import { testMockTokenAccess, testMockUrlRelative } from "~/utils/tests";
import { mockNuxtImport } from '@nuxt/test-utils/runtime';

describe('api Utils', () => {
beforeEach(() => {
vi.useFakeTimers();
});

afterEach(() => {
vi.useRealTimers();
});

it('Проверка корректной работы запросов к беку apiBack', async () => {
// ... (rest of your test code)

await vi.runAllTimersAsync();
});
});
For more information on configuring timeouts and using fake timers in Vitest, you can refer to the Vitest documentation (https://vitest.dev/config/#testtimeout). Remember, while increasing the timeout can solve the immediate issue, it's generally better to optimize your tests to run faster if possible. Long-running tests can slow down your development process and make your test suite less maintainable in the long run. Docs > Getting-started > Testing (https://nuxt.com/docs/getting-started/testing) provides more general information about testing in Nuxt 3, which might be helpful in optimizing your tests. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai3w ago
<@740173469404364872> Kapa.ai is still learning and improving, please let me know how I did by reacting below.

Did you find this page helpful?