在异步 useEffect 和 async Redux-Saga 中测试和模拟获取



我正在测试一个使用React-Hooks和Redux-Saga的功能组件。我可以在组件的 URL 中传递参数,因为它们是登录页面组件。

我传递的 URL 是"本地主机/访问/参数",当这个参数存在时,我需要调用一个异步 redux saga,如果获取正常,我将结果放在 redux-store 中。当结果在 redux-store 上时,我有一个 useEffect 来验证结果,如果正常,我会把她放在一个输入中。

我可以用 axios 模拟结果,但我正在迁移以仅使用fetch。 我嘲笑获取,但是当我使用mount(component),由酶提供,我不知道如何等待 redux-saga termine 请求和使用效果做你的工作。我将控制台日志放入效果,传奇并记录输入道具以查看您的值道具,但该值始终为空。我尝试使用setImmediate()process.nextTick().

我用来编写代码的链接:1,2,3

我正在使用formik,所以他们把一些道具传给我。

我的组件

const Login = ({
setFieldError, errors, response, fetchDomain, location, values, handleChange, handleBlur, setFieldValue, history,
}) => {
useEffect(() => {
async function fetchUrlDomain() {
const { pathname } = location;
const [, , domain] = pathname.split('/');
if (typeof domain !== 'undefined') {
await fetchDomain(domain);
}
}
fetchUrlDomain();
}, [fetchDomain, location]);
useEffect(() => {
if (typeof response === 'string') {
setFieldError('domain', 'Domain not found');
inputDomain.current.focus();
} else if (Object.keys(response).length > 0) {
setFieldValue('domain', response.Domain);
setFieldError('domain', '');
}
}, [response, setFieldValue, setFieldError]);
return (
<input name="domain" id="domain" value={values.domain} onChange={handleChange} onBlur={handleBlur} type="text" />
);
}
const LoginFormik = withFormik({
mapPropsToValues: () => ({ domain: '' }),
enableReinitialize: false,
validateOnBlur: false,
validateOnChange: false,
})(Login);
const mapStateToProps = () => ({ });
const mapDispatchToProps = dispatch => ({
fetchDomain: (value) => {
dispatch(action({}, constants.RESET_RESPONSE_DOMAIN));
dispatch(action(value, constants.FETCH_DOMAIN_REQUEST));
},
});
export default connect(mapStateToProps, mapDispatchToProps)(LoginFormik);

我的传奇

export function* fetchDomain(action) {
const url = yield `${mainUrl}/${action.payload}`;
try {
const response = yield fetch(url).then(res => res.json());
yield put(reduxAction(response , constants.FETCH_DOMAIN_SUCCESS));
} catch (e) {
yield put(reduxAction(e, constants.FETCH_DOMAIN_FAILURE));
}
}

我的减速机

case constants.FETCH_DOMAIN_FAILURE:
return { ...initialState, response: 'Domain not found' };
case constants.FETCH_DOMAIN_SUCCESS: {
const { payload } = action;
return {
...initialState,
id: payload.Id,
apis: payload.Apis,
response: payload,
};
}
case constants.RESET_RESPONSE_DOMAIN:
return { ...initialState };

我的测试

it('input with fetch only', (done) => {
const mockSuccessResponse = {
Id: 'fafafafa',
Apis: [],
Domain: 'NAME',
};
const mockJsonPromise = Promise.resolve(mockSuccessResponse);
const mockFetchPromise = Promise.resolve({
json: () => mockJsonPromise,
});
global.fetch = jest.fn().mockImplementation(() => mockFetchPromise);
const wrapper = mount(
<Provider store={store}>
<LoginForm
history={{ push: jest.fn() }}
location={{ pathname: 'localhost/login/Domain' }}
/>
</Provider>,
);
process.nextTick(() => {
const input = wrapper.find('#domain');
console.log(input.props());
expect(input.props().value.toLowerCase()).toBe('name');
global.fetch.mockClear();
done();
});
});

我希望我的输入有价值,但他没有。我尝试使用 jest-fetch-mock 但就是不起作用,我想使用本机 jest 方法,而不是三十方库。

我不能说你当前的代码有什么问题。但想提出不同的方法。

目前,您正在测试redux部分和组件的一部分。它与单元测试策略相矛盾,理想情况下你应该模拟测试下模块以外的所有内容。

所以我的意思是,如果你专注于测试组件本身,它会更容易(更少的模拟创建)和更具可读性。为此,您需要额外导出未包装的组件(在您的情况下Login)。然后,您可以仅测试其道具与渲染结果:

it('calls fetchDomain() with domain part of location', () => {
const fetchDomain = jest.fn();
const location = { pathName: 'example.com/path/sub' }
shallow(<Login fetchDomain={fetchDomain} location={location} />);
expect(fetchDomain).toHaveBeenCalledTimes(1);
expect(fetchDomain).toHaveBeenCalledWith('example.com');
});
it('re-calls fetchDomain() on each change of location prop', () => {
const fetchDomain = jest.fn();
const location = { pathName: 'example.com/path/sub' }
const wrapper = shallow(<Login fetchDomain={fetchDomain} location={location} />);
fetchDomain.mockClear();
wrapper.setProps({ location: { pathName: 'another.org/path' } });
expect(fetchDomain).toHaveBeenCalledTimes(1);
expect(fetchDomain).toHaveBeenCalledWith('another.org');
});

其他情况也是如此。如果您将redux替换为直接调用fetch()或其他任何东西,或者如果您重构该数据以来自父级而不是从 redux 存储读取,则无需重写测试以删除对 redux 的模拟,请参阅此方法。当然,您仍然需要测试 redux 部分,但它也可以单独完成。

PS 并且useEffect没有利润可以await fetchDomain(...),因为您不使用它返回的内容。await不像暂停那样工作,该代码可能会使读者感到困惑。

最新更新