Я тоже не мог прочитать все redux-saga
ресурсы для тестирования ... не нашел подходящего решения (по крайней мере, для меня).
То, что я закончил, это:
- Я "визуализирую" пустое приложение React с работающим магазином
- Я вручную запускаю интересные действия (те, которые запускают саги, которые я тестирую)
- Я отслеживаю все внешние ресурсы, потребляемые сагами
В конце: я запускаю саги, и шпионю, что они вызывают другие вещи.
Я считаю саги черным ящиком и проверяю, что они соблюдают договор со всеми остальными частями приложения.
Я беру пример из моих тестов sagas для аутентификации (я сломал много тестов передовой практики, я знаю, это происходит из моих ранних дней тестирования с помощью saga) (см. Ниже для функций renderWithRedux
и spyUtil
):
describe("Login flow with valid credentials", () => {
let user = "stefano";
let pwd = "my_super_secret_password";
let app;
let spies;
// const spiedConsole = spyConsole();
beforeAll(() => {
app = renderWithRedux(<></>);
spies = {
LOGIN_SUCCESS_creator: spyUtil(authActions, "LOGIN_SUCCESS_creator"),
navigate: spyUtil(ReachRouter, "navigate"),
postLogin: spyUtil(authNetwork, "postLogin", postLoginOk),
redirectBackFromLoginPage: spyUtil(navigationData, "redirectBackFromLoginPage")
};
});
test("1 - the login API should be called as soon as the LOGIN_REQUEST action is dispatched", async () => {
app.store.dispatch(authActions.LOGIN_REQUEST_creator(user, pwd));
expect(spies.postLogin.spy).toHaveBeenCalledWith(user, pwd);
});
test("2 - then when the login API is successfull, a LOGIN_SUCCESS action should be dispatched with the tokens", async () => {
expect(spies.LOGIN_SUCCESS_creator.spy).toHaveBeenCalledWith(
expect.any(String),
expect.any(String)
);
});
test("3 - then the router should be asked to make a redirect to the initial location", async () => {
expect(spies.redirectBackFromLoginPage.spy).toHaveBeenCalled();
expect(spies.navigate.spy).toHaveBeenCalledWith(expect.stringMatching(/\//));
});
afterAll(() => {
spies.values().forEach(obj => obj.spy.mockRestore());
// spiedConsole.mockRestore();
cleanup();
});
});
Шаг за шагом:
- я рендерил пустое приложение с работающим магазином Redux + Saga
app = renderWithRedux(<></>);
- Я все замечаю за пределами саг
spies = {
LOGIN_SUCCESS_creator: spyUtil(authActions, "LOGIN_SUCCESS_creator"),
navigate: spyUtil(ReachRouter, "navigate"),
postLogin: spyUtil(authNetwork, "postLogin", postLoginOk),
redirectBackFromLoginPage: spyUtil(navigationData, "redirectBackFromLoginPage")
};
где:
test("1 - the login API should be called as soon as the LOGIN_REQUEST action is dispatched", async () => {
app.store.dispatch(authActions.LOGIN_REQUEST_creator(user, pwd));
expect(spies.postLogin.spy).toHaveBeenCalledWith(user, pwd);
});
- Я проверяю, что действие
LOGIN_SUCCESS
будет отправлено с токенами аутентификации
test("2 - then when the login API is successfull, a LOGIN_SUCCESS action should be dispatched with the tokens", async () => {
expect(spies.LOGIN_SUCCESS_creator.spy).toHaveBeenCalledWith(
expect.any(String),
expect.any(String)
);
});
- Я проверяю, что маршрутизатор был вызван с правильным маршрутом (
/
для домашней страницы)
test("3 - then the router should be asked to make a redirect to the initial location", async () => {
expect(spies.redirectBackFromLoginPage.spy).toHaveBeenCalled();
expect(spies.navigate.spy).toHaveBeenCalledWith(expect.stringMatching(/\//));
});
afterAll(() => {
spies.values().forEach(obj => obj.spy.mockRestore());
// spiedConsole.mockRestore();
cleanup();
});
Это «мое» (исходит от Кента С. Доддса) renderWithRedux
функция
// @see https://github.com/kentcdodds/react-testing-library/blob/master/examples/__tests__/react-redux.js
export function renderWithRedux(ui, { initialState, store = configureStore() } = {}) {
return {
...render(
<div>
<Provider store={store}>{ui}</Provider>
</div>
),
// adding `store` to the returned utilities to allow us
// to reference it in our tests (just try to avoid using
// this to test implementation details).
store
};
}
где configureStore
- моя функция, которая создает весь магазин Redux с различными промежуточными программами.
Это моя spyUtil
функция
/**
* A all-in-one spy and mock function
* @param {object} obj
* @param {string} name
* @param {function} mockFunction
*/
export function spyUtil(obj, name, mockFunction = undefined) {
const spy = jest.spyOn(obj, name);
let mock;
if (mockFunction) {
mock = jest.fn(mockFunction);
obj[name].mockImplementation(mock);
}
return { spy, mock };
}
Обратите внимание, что это только один из процессов аутентификации, я не сообщил здесь обо всех случаях.
Мне бы хотелось узнать ваши мысли об этом 10