Issue
I'm trying to test the following slice (simplified version without right typing):
interface MyState {
data: any[];
}
const initialState: MyState = {
data: [],
};
export const postData = createAsyncThunk(
'data/postData',
async (
param: {
data: any[];
},
{ rejectWithValue },
) => {
try {
const myService: MyService = new MyService();
return await myService.postData(param.data);
} catch (error) {
console.error(error.message);
return rejectWithValue(error.message);
}
},
);
const combineExtraReducers = (
builder: ActionReducerMapBuilder<MyState>,
) => {
builder.addCase(getData.fulfilled, (state, { payload }) => {
state.data = payload;
});
};
export const dataSlice = createSlice({
name: 'data',
initialState: initialState,
reducers: {},
extraReducers: (builder) => {
combineExtraReducers(builder);
},
});
How can I write a simple test for this slice? I'm fine without createAsyncThunk, but I can't manage to write a test with the thunk.
The approach from the official redux toolkit documentation does not work either. I get an error message regarding timeout.
We're using axios as HTTP client.
Solution
Steps:
Use
jest.spyOn(MyService.prototype, 'postData')to mockpostDatamethod ofMyServiceclass and its resolved/rejected value.Create a mock redux store so that we can dispatch the async thunk and get the state using
store.getState()API and assert the changes.
E.g.
index.ts:
import { ActionReducerMapBuilder, createAsyncThunk, createSlice } from '@reduxjs/toolkit';
import { MyService } from './my-service';
interface MyState {
data: any[];
}
const initialState: MyState = {
data: [],
};
export const postData = createAsyncThunk(
'data/postData',
async (
param: {
data: any[];
},
{ rejectWithValue },
) => {
try {
const myService: MyService = new MyService();
return await myService.postData(param.data);
} catch (error) {
console.error(error.message);
return rejectWithValue(error.message);
}
},
);
const combineExtraReducers = (builder: ActionReducerMapBuilder<MyState>) => {
builder.addCase(postData.fulfilled, (state, { payload }) => {
state.data = payload;
});
};
export const dataSlice = createSlice({
name: 'data',
initialState: initialState,
reducers: {},
extraReducers: (builder) => {
combineExtraReducers(builder);
},
});
my-service.ts:
export class MyService {
async postData(params) {
return ['real data'];
}
}
index.test.ts:
import { configureStore } from '@reduxjs/toolkit';
import { dataSlice, postData } from './';
import { MyService } from './my-service';
describe('72549837', () => {
afterEach(() => {
jest.restoreAllMocks();
});
test('should post data fulfilled', async () => {
jest.spyOn(MyService.prototype, 'postData').mockResolvedValueOnce(['mock data']);
const store = configureStore({ reducer: dataSlice.reducer });
await store.dispatch(postData({ data: ['mock arg'] }));
expect(store.getState()).toEqual({ data: ['mock data'] });
});
});
Test result:
PASS redux-toolkit-example packages/redux-toolkit-example/stackoverflow/72549837/index.test.ts
72549837
✓ should post data fulfilled (6 ms)
---------------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
---------------|---------|----------|---------|---------|-------------------
All files | 85.71 | 100 | 83.33 | 83.33 |
index.ts | 87.5 | 100 | 100 | 85.71 | 24-25
my-service.ts | 80 | 100 | 50 | 75 | 3
---------------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 3.609 s
Answered By - slideshowp2
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.