英文:
How to mock appSelector with dummy data in React redux-toolkit tests
问题
以下是您提供的代码的翻译部分:
我有以下的React函数组件:
const AssetsPage = () => {
const [filteredAssets, setFilteredAssets] = useState<Asset[]>([]);
const assetsState = useAppSelector(selectAssets);
...
useEffect(() => {
setFilteredAssets(
assetsState.assets
);
}, [assetsState.assets])
useEffect(() => {
store.dispatch(fetchAssetsAsync(null));
}, [])
const columns: GridColDef[] = [
...某些列定义...
];
return (
<>
<Typography variant="h2" ml={2}>
{t("Asset.Title")}
</Typography>
<DataTable
rows={filteredAssets}
columns={columns}
rowId="globalId"
/>
</>
)
}
export default AssetsPage
和相应的切片(slice):
import {Asset} from "../../models/Asset";
import {createAsyncThunkWithErrorHandling} from "../../utils/thunkHelper";
import {createSlice} from "@reduxjs/toolkit";
import {RootState} from "../../app/store";
import {createAsset, deleteAsset, getAssets, updateAsset} from "../../api/assets";
const initialState = {
isLoaded: false,
assets: [] as Asset[],
}
...
export const fetchAssetsAsync = createAsyncThunkWithErrorHandling(
"assets/fetch",
async (payload: string) => {
return await getAssets(payload);
}
)
...
export const oeeAssetsSlice = createSlice({
name: "assets",
initialState,
reducers: {},
extraReducers: (builder) => {
builder.addCase(fetchAssetsAsync.fulfilled, (state, action) => {
state.isLoaded = true;
state.assets = [
...action.payload,
];
});
...
}
})
export const selectAssets = (state: RootState) => state.assets;
export default oeeAssetsSlice.reducer;
这段代码用于创建React组件和相应的Redux切片。
请注意,这只是您代码的翻译部分。如果您需要更多的帮助或有其他问题,请随时提出。
英文:
I have the following react function component:
const AssetsPage = () => {
const [filteredAssets, setFilteredAssets] = useState<Asset[]>([]);
const assetsState = useAppSelector(selectAssets);
...
useEffect(() => {
setFilteredAssets(
assetsState.assets
);
}, [assetsState.assets])
useEffect(() => {
store.dispatch(fetchAssetsAsync(null));
}, [])
const columns: GridColDef[] = [
...some column definitions...
];
return (
<>
<Typography variant="h2" ml={2}>
{t("Asset.Title")}
</Typography>
<DataTable
rows={filteredAssets}
columns={columns}
rowId="globalId"
/>
</>
)
}
export default AssetsPage
And the corresponding slice:
import {Asset} from "../../models/Asset";
import {createAsyncThunkWithErrorHandling} from "../../utils/thunkHelper";
import {createSlice} from "@reduxjs/toolkit";
import {RootState} from "../../app/store";
import {createAsset, deleteAsset, getAssets, updateAsset} from "../../api/assets";
const initialState = {
isLoaded: false,
assets: [] as Asset[],
}
...
export const fetchAssetsAsync = createAsyncThunkWithErrorHandling(
"assets/fetch",
async (payload: string) => {
return await getAssets(payload);
}
)
...
export const oeeAssetsSlice = createSlice({
name: "assets",
initialState,
reducers: {},
extraReducers: (builder) => {
builder.addCase(fetchAssetsAsync.fulfilled, (state, action) => {
state.isLoaded = true;
state.assets = [
...action.payload,
];
});
...
}
})
export const selectAssets = (state: RootState) => state.assets;
export default oeeAssetsSlice.reducer;
This works just fine, it loads data from the backend (or, if I replace the fetchAssetsAsync
implementation with static data, it loads it properly.)
I am trying to do some unit testing. Rendering the empty table works fine: but I cannot inject data to the assetsState
no matter how I try.
import {fireEvent, render, screen, waitFor} from "@testing-library/react";
import {Provider} from "react-redux";
import {RootState, store} from "../../../app/store";
import AssetsPage from "./AssetsPage";
import {createTheme} from "@mui/material/styles";
import {ThemeOptions} from "../../ThemeOptions/ThemeOptions";
import {ThemeProvider} from "@mui/material";
import oeeAssetsReducer, {selectAssets} from "../../../features/assets/oeeAssetsSlice";
import {useAppSelector} from "../../../app/hooks";
import appReducer from "../../../features/app/appSlice";
import userReducer from "../../../features/user/userSlice";
describe("AssetListPage", () => {
const theme = createTheme(ThemeOptions);
// This test works fine.
test("renders the empty component", async () => {
render(
<ThemeProvider theme={theme}>
<Provider store={store}>
<AssetsPage/>
</Provider>
</ThemeProvider>
);
expect(screen.getByText("No rows")).toBeInTheDocument();
});
// todo: this doesnt work.
const asset = {
globalId: "asset-1",
description: "test asset",
businessUnitCode: "bu-1",
localId: "123",
enabledFlag: 1,
};
jest.mock("../../../app/hooks", () => ({
useAppSelector: (selectAssets: any) => ({
assetsState: {
isLoaded: true,
assets: [asset]
}
})
}));
jest.mock("react-i18next", () => ({
useTranslation: () => ({
t: (key: string) => key
})
}));
test("renders the component with one data line", async () => {
render(<ThemeProvider theme={theme}>
<Provider store={store}>
<AssetsPage/>
</Provider>
</ThemeProvider>
);
render(
<ThemeProvider theme={theme}>
<Provider store={store}>
<AssetsPage/>
</Provider>
</ThemeProvider>
);
expect(screen.getByText("Asset.GlobalAssetId")).toBeInTheDocument();
expect(screen.getByText("Asset.Description")).toBeInTheDocument();
expect(screen.getByText("Asset.BusinessUnit")).toBeInTheDocument();
expect(screen.getByText("Asset.LocalId")).toBeInTheDocument(); // it renders the "empty table" instead
expect(screen.getByText("Asset.Disable")).toBeInTheDocument();
expect(screen.getByText("Asset.Actions")).toBeInTheDocument();
})
});
The test always renders a table without any data. What am I missing here? I cannot install Enzyme, tried feeding this to chatGPT, but nothing worked so far.
答案1
得分: 0
这是一个可能的解决方案:
test("呈现带有一条数据行的空组件", async () => {
const mockUser = getAWhoamiSlice_SUPERADMIN();
const mockShifts = getATestShiftsReducer_EMTPY();
const mockBusinessUnits = getBusinessUnitsReducer_EMPTY();
const mockAssets = getAssetsReducer_TWO_ITEMS();
const mockStore = configureStore({
middleware: undefined,
reducer: {
whoami: whoamiReducer,
shifts: oeeShiftsReducer,
businessUnits: oeeBusinessUnitReducer,
assets: oeeAssetsReducer,
},
preloadedState: {
whoami: mockUser,
shifts: mockShifts,
businessUnits: mockBusinessUnits,
assets: mockAssets,
},
});
render(
<ThemeProvider theme={theme}>
<Provider store={mockStore}>
<AssetsPage/>
</Provider>
</ThemeProvider>
);
expect(screen.queryByText("没有行")).not.toBeInTheDocument();
expect(screen.getByText("Asset.GlobalAssetId")).toBeInTheDocument();
expect(screen.getByText("Asset.Description")).toBeInTheDocument();
expect(screen.queryByText(mockAssets.assets[0].name)).toBeInTheDocument();
})
关于未显示函数的一些解释:
- Reducers是应用程序中使用的相同reducers。然而,在测试中,我们使用初始数据预加载了状态,而不是实际使用reducers。
- 预加载状态的函数返回的是类似于通过REST调用获取的数据。
英文:
Here is a possible solution:
test ("render the emtpy component with one data line", async () => {
const mockUser = getAWhoamiSlice_SUPERADMIN()
const mockShifts = getATestShiftsReducer_EMTPY()
const mockBusinessUnits =getBusinessUnitsReducer_EMPTY()
const mockAssets = getAssetsReducer_TWO_ITEMS()
const mockStore = configureStore({
middleware: undefined,
reducer: {
whoami: whoamiReducer,
shifts: oeeShiftsReducer,
businessUnits: oeeBusinessUnitReducer,
assets: oeeAssetsReducer,
},
preloadedState: {
whoami: mockUser,
shifts: mockShifts,
businessUnits: mockBusinessUnits,
assets: mockAssets
},
});
render(
<ThemeProvider theme={theme}>
<Provider store={mockStore}>
<AssetsPage/>
</Provider>
</ThemeProvider>
);
expect(screen.queryByText("No rows")).not.toBeInTheDocument();
expect(screen.getByText("Asset.GlobalAssetId")).toBeInTheDocument();
expect(screen.getByText("Asset.Description")).toBeInTheDocument();
expect(screen.queryByText(mockAssets.assets[0].name)).toBeInTheDocument();
})
Some explanation for the unseen functions:
- The reducers are the same reducers used in the application. They are not used however, we preload the state for the tests with initial data.
- the preloaded state functions are returning similar data which would arrive otherwise with the REST calls.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论