Jest先前测试结果泄漏到下一个测试中

gg0vcinb  于 6个月前  发布在  Jest
关注(0)|答案(1)|浏览(75)

我已经为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.jsjest.clearAllMocks(),但也没有帮助。
因此,我的第一个测试用例是安慰'notificationsHandler starting a fresh'和下一个2 notificationsHandler already exists

tgabmvqs

tgabmvqs1#

Nux的评论来看,问题可能与notificationsHandler和其他变量在导入时如何初始化有关。
这种方法可能会导致跨测试共享状态和意外行为,正如您所经历的那样。
在JavaScript和Node.js模块中,任何不在函数或类内部的代码都会在导入时立即执行。这可能会导致测试环境中出现问题,因为您希望每个测试都从一个干净的状态开始。
为了避免这种情况,你应该重构你的模块以避免导入时的副作用。不要直接执行代码,而是将行为封装在函数或类中。这样,你就可以控制代码何时以及如何执行。
而且,与其在模块级别初始化像notificationsHandler这样的变量,不如提供一个函数来初始化它们。该函数可以在需要时显式调用,以确保更好地控制状态。
重构后的模块应该是:

// Before refactoring
let notificationsHandler;
export let notificationsHandlerExists = false;
// other code that initializes notificationsHandler

// After refactoring
let notificationsHandler;
export let notificationsHandlerExists = false;

export function initializeNotificationsHandler(user) {
  if (!notificationsHandler) {
    notificationsHandler = new Notifications(user.id, /* */);
  } else {
    notificationsHandlerExists = true;
  }
}

// In your endpoint
endpoints: (builder) => ({
  fetchNotifications: builder.query({
    // 
    onQueryStarted: async (_, { dispatch, getState }) => {
      const user = getState().application.user;
      initializeNotificationsHandler(user);
      // 
    }
  }),
})

字符串
initializeNotificationsHandler是您在onQueryStarted中显式调用的函数。它避免了在导入时初始化notificationsHandler的问题。
在测试中,您现在可以更好地控制notificationsHandler的初始化时间。您可以选择在每个测试中显式调用initializeNotificationsHandler或根据需要模拟它。这种方法应该可以解决测试之间的共享状态问题,因为notificationsHandler的初始化现在将在您的控制之下,在测试的执行上下文中发生,而不是在模块导入时发生。

相关问题