我已经为createAPI编写了这个RTK切片。
// notificationApi.slice.js
import config from '../../config/default'
import { prepareHeaders } from '@/utils/request'
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
import { PAGE_SIZE } from '@/constants/notifications'
import { mapNotification } from '@/utils/notification'
import Notifications from '../../services/notifications'
const NOTIFICATION_URI = '/scheduler/'
const url = (config.apiGatewayV2 || config.apiGateway) + NOTIFICATION_URI
let notificationsHandler;
export let notificationsHandlerExists = false;
export const notificationApi = createApi({
reducerPath: 'notificationApi',
baseQuery: fetchBaseQuery({
baseUrl: `${url}`,
prepareHeaders,
}),
endpoints: (builder) => ({
fetchNotifications: builder.query({
query: (pageNumber) => {
const pageSize = PAGE_SIZE
return {
url: 'notifications',
params: { pageNumber, pageSize },
}
},
transformResponse: (response) => {
if (!response.data) {
return response
}
response.data = response.data.map((item) => mapNotification(item))
return response
},
transformErrorResponse: (response) => {
console.error(response.error)
return response.status
},
forceRefetch: () => {
return true
},
onQueryStarted: async (_, { dispatch, getState }) => {
const user = getState().application.user;
if (!notificationsHandler) {
console.log('notificationsHandler starting a fresh')
notificationsHandler = new Notifications(user.id, async () => {
await notificationsHandler.open();
// Trigger the fetchNotifications query
await dispatch(notificationApi.endpoints.fetchNotifications.initiate(undefined));
});
} else {
console.log('notificationsHandler already exists')
notificationsHandlerExists = true;
}
}
}),
}),
})
export const { useFetchNotificationsQuery } = notificationApi
字符串
然后我的测试用例如下所示
import { renderHook, waitFor, act } from '@testing-library/react'
import { useFetchNotificationsQuery, notificationsHandlerExists, notificationApi } from './notificationApi.slice'
import moment from 'moment'
import { renderWithProviders } from '@/utils/test.utils'
import fetchMock from 'jest-fetch-mock'
import Notifications from '../../services/notifications';
fetchMock.enableMocks()
jest.mock('@/utils/request', () => ({}))
jest.mock('../../services/notifications');
beforeEach(() => {
fetchMock.resetMocks()
jest.clearAllMocks()
})
describe('useFetchNotificationsQuery', () => {
beforeEach(() => {
jest.resetModules();
fetchMock.resetMocks()
jest.clearAllMocks()
})
describe('When notification api response error', () => {
it('Should return object with data as undefined', async () => {
fetchMock.mockReject(new Error('Internal Server Error'))
const { Wrapper: wrapper } = await renderWithProviders('')
const { result } = renderHook(() => useFetchNotificationsQuery(1), {
wrapper,
})
await act(async () => {
const initialResponse = result.current
expect(initialResponse.data).toBeUndefined()
expect(initialResponse.isLoading).toBe(true)
await waitFor(() => {
const nextResponse = result.current
expect(nextResponse.data).toBeUndefined()
expect(nextResponse.isLoading).toBe(true)
expect(nextResponse.isError).toBe(false)
})
})
})
})
describe('onQueryStarted callback', () => {
beforeEach(() => {
jest.resetModules();
fetchMock.resetMocks()
jest.clearAllMocks()
})
it('should initialize NotificationsHandler if not exists', async () => {
const { Wrapper: wrapper } = renderWithProviders('')
const { result } = renderHook(() => useFetchNotificationsQuery(1), {
wrapper,
})
await act(async () => {
// Mock the getState function to return the required state
notificationApi.getState = jest.fn(() => ({
application: {
user: {
id: 'mockUserId',
},
},
}));
// Call the query function
await result.current[0];
// Assert that NotificationsHandler is initialized
expect(fetchMock).toHaveBeenCalledTimes(1); // Assuming the fetchNotifications query triggers a fetch
// Add more assertions based on your specific implementation details
// Verify that the Notifications constructor is called
expect(Notifications).toHaveBeenCalledTimes(1);
// Optionally, you can also check the constructor arguments or other details
expect(Notifications).toHaveBeenCalledWith('mockUserId', expect.any(Function));
});
})
it('should use existing NotificationsHandler if it already exists', async () => {
// Simulate an existing notificationsHandler
let notificationsHandler = new Notifications('existingUserId', jest.fn());
const { Wrapper: wrapper } = renderWithProviders(''); // You need to implement renderWithProviders function
const { result } = renderHook(() => useFetchNotificationsQuery(1), {
wrapper,
});
await act(async () => {
// Mock the getState function to return the required state
getState = jest.fn(() => ({
notificationApi: {
application: {
user: {
id: 'mockUserId',
},
},
},
}));
// Call the query function
await result.current[0];
// Expect notificationsHandlerExists flag to be false
expect(notificationsHandlerExists).toBe(true)
// Assert that NotificationsHandler is not initialized again
expect(fetchMock).toHaveBeenCalledTimes(1)
expect(Notifications).toHaveBeenCalledTimes(1) // Simulation of existing notificationsHandler
})
})
})
})
型
这是发生了什么,所以在这里RTK createAPI slice
,在onQueryStarted callback
中,我检查notificationsHandler
是否已经存在。第一个测试用例'When notification api response error'
检查notificationsHandler
不存在,并创建一个。第二个测试用例'should initialize notificationsHandler if not exists'
再次出现在onQueryStarted callback
中,这次它看到以前创建的notificationsHandler
已经存在,并且持久性,因此跳过该测试用例正在失败。
如何解决这个问题。我试着在beforeEach
上嘲笑notificationApi.slice.js
和jest.clearAllMocks()
,但也没有帮助。
因此,我的第一个测试用例是安慰'notificationsHandler starting a fresh'
和下一个2 notificationsHandler already exists
。
1条答案
按热度按时间tgabmvqs1#
从Nux的评论来看,问题可能与
notificationsHandler
和其他变量在导入时如何初始化有关。这种方法可能会导致跨测试共享状态和意外行为,正如您所经历的那样。
在JavaScript和Node.js模块中,任何不在函数或类内部的代码都会在导入时立即执行。这可能会导致测试环境中出现问题,因为您希望每个测试都从一个干净的状态开始。
为了避免这种情况,你应该重构你的模块以避免导入时的副作用。不要直接执行代码,而是将行为封装在函数或类中。这样,你就可以控制代码何时以及如何执行。
而且,与其在模块级别初始化像
notificationsHandler
这样的变量,不如提供一个函数来初始化它们。该函数可以在需要时显式调用,以确保更好地控制状态。重构后的模块应该是:
字符串
initializeNotificationsHandler
是您在onQueryStarted
中显式调用的函数。它避免了在导入时初始化notificationsHandler
的问题。在测试中,您现在可以更好地控制
notificationsHandler
的初始化时间。您可以选择在每个测试中显式调用initializeNotificationsHandler
或根据需要模拟它。这种方法应该可以解决测试之间的共享状态问题,因为notificationsHandler
的初始化现在将在您的控制之下,在测试的执行上下文中发生,而不是在模块导入时发生。