gpt4 book ai didi

reactjs - 使用 redux thunk 测试异步操作

转载 作者:行者123 更新时间:2023-12-03 13:23:04 25 4
gpt4 key购买 nike

我正在尝试测试具有异步调用的操作。我使用 Thunk 作为我的中间件。在下面的操作中,我只在服务器返回 OK 时才调度和更新商店。回复。

export const SET_SUBSCRIBED = 'SET_SUBSCRIBED'

export const setSubscribed = (subscribed) => {
return function(dispatch) {
var url = 'https://api.github.com/users/1/repos';

return fetch(url, {method: 'GET'})
.then(function(result) {
if (result.status === 200) {
dispatch({
type: SET_SUBSCRIBED,
subscribed: subscribed
})
return 'result'
}
return 'failed' //todo
}, function(error) {
return 'error'
})
}
}

我在编写测试时遇到问题,无论是调用还是不调用调度的测试(取决于服务器响应),或者我可以让操作被调用并检查存储中的值是否正确更新。

我正在使用 fetch-mock 来模拟网络的 fetch() 实现。但是,它看起来像我在 then 中的代码块不执行。我也试过在这里使用这个例子,但没有成功 - http://redux.js.org/docs/recipes/WritingTests.html

const middlewares = [ thunk ]
const mockStore = configureStore(middlewares)

//passing test
it('returns SET_SUBSCRIBED type and subscribed true', () => {
fetchMock.get('https://api.github.com/users/1/repos', { status: 200 })

const subscribed = { type: 'SET_SUBSCRIBED', subscribed: true }
const store = mockStore({})

store.dispatch(subscribed)

const actions = store.getActions()

expect(actions).toEqual([subscribed])
fetchMock.restore()
})

//failing test
it('does nothing', () => {
fetchMock.get('https://api.github.com/users/1/repos', { status: 400 })

const subscribed = { type: 'SET_SUBSCRIBED', subscribed: true }
const store = mockStore({})

store.dispatch(subscribed)

const actions = store.getActions()

expect(actions).toEqual([])
fetchMock.restore()
})

在对此进行了更多研究之后,我认为 fetch-mock 存在问题,要么没有解决 promise ,以便 then 语句执行,要么完全阻止了 fetch。当我向两个 then 语句添加一个 console.log 时,什么都不执行。

我在测试中做错了什么?

最佳答案

在 Redux 中测试异步 Thunk 操作

您没有在任何测试中调用 setSubscribed redux-thunk 操作创建者。相反,您正在定义一个相同类型的新操作并尝试在您的测试中分派(dispatch)它。

在您的两个测试中,正在同步调度以下操作。

  const subscribed = { type: 'SET_SUBSCRIBED', subscribed: true }

在此操作中,不会向任何 API 发出请求。

我们希望能够从外部 API 获取数据,然后在成功或失败时分派(dispatch)操作。

由于我们将在 future 的某个时间分派(dispatch) Action ,因此我们需要使用您的 setSubscribed thunk Action 创建者。

在简要解释了 redux-thunk 的工作原理之后,我将解释如何测试这个 thunk Action 创建器。

Action 与 Action 创作者

也许值得解释一下, Action 创建者是一个在调用时返回 Action 对象的函数。

术语操作是指对象本身。对于这个 Action 对象,唯一的强制属性是 type,它应该是一个字符串。

例如这里是 Action 创建者 .
function addTodo(text) {
return {
type: ADD_TODO,
text
}
}

它只是一个返回对象的函数。 我们知道这个对象是一个 redux action,因为它的一个属性叫做 type。

它创建 toDos 以按需添加。让我们做一个新的待办事项来提醒我们遛狗。
const walkDogAction = addTodo('walk the dog')

console.log(walkDogAction)
*
* { type: 'ADD_TO_DO, text: 'walk the dog' }
*

此时我们有一个 Action 对象,它是 生成 由我们的 Action 创建者。

现在,如果我们想将此操作发送到我们的 reducer 以更新我们的存储,那么我们将使用 action 对象作为参数调用 dispatch。
store.dispatch(walkDogAction)

伟大的。

我们已经发送了对象,它将直接发送到 reducer 并使用新的待办事项更新我们的商店,提醒我们遛狗。

我们如何做出更复杂的 Action ?如果我想让我的 Action 创建者做一些依赖于异步操作的事情怎么办。

同步与异步 Redux 操作

async(异步)和sync(同步)是什么意思?

When you execute something synchronously, you wait for it to finish before moving on to another task. When you execute something asynchronously, you can move on to another task before it finishes.



好的,那么如果我想让我的狗去拿东西呢?在这种情况下,我关心三件事
  • 当我让他拿东西时
  • 他取东西成功了吗?
  • 他没能拿到那个对象吗? (即没有棍子回到我身边,在给定的时间后根本没有回到我身边)

  • 可能很难想象这是如何用一个对象来表示的,比如我们遛狗的 addtodo Action ,它只包含一个类型和一段文本。

    而不是 Action 是一个对象,它需要是一个函数。为什么是函数?函数可用于分派(dispatch)进一步的 Action 。

    我们将 fetch 的主要操作拆分为三个较小的同步操作。我们主要的 fetch Action 创建者是异步的。请记住,这个主要的 Action 创建者本身并不是一个 Action ,它只是为了调度进一步的 Action 而存在。

    Thunk Action 创建器是如何工作的?

    本质上,thunk Action 创建者是返回函数而不是对象的 Action 创建者。通过将 redux-thunk 添加到我们的中间件存储中,这些特殊操作将可以访问存储的 dispatch 和 getState 方法。
    Here is the code inside Redux thunk that does this:

    if (typeof action === 'function') {
    return action(dispatch, getState, extraArgument);
    }

    setSubscribed 函数是一个 thunk Action 创建者,因为它遵循返回一个将 dispatch 作为参数的函数的签名。

    好的,这就是我们的 thunk Action 创建者返回一个函数的原因。因为这个函数会被中间件调用,让我们可以访问调度和获取状态,这意味着我们可以在以后调度进一步的 Action 。

    使用 Action 建模异步操作

    让我们写下我们的行动。我们的 redux thunk Action 创建者负责异步分派(dispatch)其他三个 Action ,它们代表 aour async Action 的生命周期,在这种情况下是一个 http 请求。请记住,此模型适用于任何异步操作,因为必须有一个开始和一个标志着成功或某些错误(失败)的结果

    Action .js
    export function fetchSomethingRequest () {
    return {
    type: 'FETCH_SOMETHING_REQUEST'
    }
    }

    export function fetchSomethingSuccess (body) {
    return {
    type: 'FETCH_SOMETHING_SUCCESS',
    body: body
    }
    }

    export function fetchSomethingFailure (err) {
    return {
    type: 'FETCH_SOMETHING_FAILURE',
    err
    }
    }

    export function fetchSomething () {
    return function (dispatch) {
    dispatch(fetchSomethingRequest())
    return fetchSomething('http://example.com/').then(function (response) {
    if (response.status !== 200) {
    throw new Error('Bad response from server')
    } else {
    dispatch(fetchSomethingSuccess(response))
    }
    }).catch(function (reason) {
    dispatch(fetchSomethingFailure(reason))
    })
    }
    }

    您可能知道最后一个 Action 是 redux thunk Action 创建者。我们知道这一点,因为它是唯一返回函数的操作。

    创建我们的 Mock Redux 存储

    在测试文件中,从 redux-mock-store 库中导入 configure store 函数来创建我们的假存储。
    import configureStore from 'redux-mock-store';

    这个模拟存储将在一个数组中分派(dispatch)的 Action 用于你的测试。

    由于我们正在测试一个 thunk Action 创建者,我们的模拟存储需要在我们的测试中使用 redux-thunk 中间件进行配置,否则我们的商店将无法处理 thunk Action 创建者。或者换句话说,我们将无法调度函数而不是对象。
    const middlewares = [ReduxThunk];
    const mockStore = configureStore(middlewares);

    Out mock store 有一个 store.getActions 方法,当调用该方法时,它会为我们提供一个包含所有先前调度操作的数组。

    然后,我们进行测试断言,以比较发送到模拟存储的实际操作与我们预期的操作。

    在 Mocha 中测试我们的 thunk Action 创建者返回的 promise

    所以在测试结束时,我们将我们的 thunk Action 创建者分派(dispatch)到模拟商店。我们一定不要忘记返回这个调度调用,这样当 thunk 操作创建者返回的 promise 被解析时,断言将在 .then 块中运行。

    工作测试

    如果您使用上述操作将此测试文件复制到您的应用程序中,确保安装所有包并正确导入以下测试文件中的操作,那么您将有一个测试 redux thunk 操作创建者的工作示例,以确保它们分派(dispatch)正确的行动。
    import configureMockStore from 'redux-mock-store'
    import thunk from 'redux-thunk'
    import fetchMock from 'fetch-mock' // You can use any http mocking library
    import expect from 'expect' // You can use any testing library

    import { fetchSomething } from './actions.js'

    const middlewares = [ thunk ]
    const mockStore = configureMockStore(middlewares)

    describe('Test thunk action creator', () => {
    it('expected actions should be dispatched on successful request', () => {
    const store = mockStore({})
    const expectedActions = [
    'FETCH_SOMETHING_REQUEST',
    'FETCH_SOMETHING_SUCCESS'
    ]

    // Mock the fetch() global to always return the same value for GET
    // requests to all URLs.
    fetchMock.get('*', { response: 200 })

    return store.dispatch(fetchSomething())
    .then(() => {
    const actualActions = store.getActions().map(action => action.type)
    expect(actualActions).toEqual(expectedActions)
    })

    fetchMock.restore()
    })

    it('expected actions should be dispatched on failed request', () => {
    const store = mockStore({})
    const expectedActions = [
    'FETCH_SOMETHING_REQUEST',
    'FETCH_SOMETHING_FAILURE'
    ]
    // Mock the fetch() global to always return the same value for GET
    // requests to all URLs.
    fetchMock.get('*', { response: 404 })

    return store.dispatch(fetchSomething())
    .then(() => {
    const actualActions = store.getActions().map(action => action.type)
    expect(actualActions).toEqual(expectedActions)
    })

    fetchMock.restore()
    })
    })

    请记住,因为我们的 Redux thunk Action 创建者本身不是 Action ,它的存在只是为了调度进一步的 Action 。

    我们对 thunk Action 创建者的大部分测试将集中在对在特定条件下调度的额外 Action 进行断言。

    这些特定条件是异步操作的状态,可能是超时的 http 请求或代表成功的 200 状态。

    测试 Redux Thunk 时的常见问题 - 不返回行动创造者的 promise

    始终确保在为 Action 创建者使用 promise 时 返回 Action 创建者返回的函数内的 promise 。
        export function thunkActionCreator () {
    return function thatIsCalledByreduxThunkMiddleware() {

    // Ensure the function below is returned so that
    // the promise returned is thenable in our tests
    return function returnsPromise()
    .then(function (fulfilledResult) {
    // do something here
    })
    }
    }

    因此,如果最后一个嵌套函数没有返回,那么当我们尝试异步调用该函数时,我们将得到错误:
    TypeError: Cannot read property 'then' of undefined - store.dispatch - returns undefined

    那是因为我们试图在 .then 子句中实现或拒绝 promise 后做出断言。但是 .then 不起作用,因为我们只能在 promise 上调用 .then 。由于我们忘记返回 action creator 中的最后一个嵌套函数,它返回一个 promise,那么我们将在 undefined 上调用 .then。之所以未定义,是因为函数作用域内没有return语句。

    所以总是在 Action 创建者中返回函数,当被调用时返回 promise 。

    关于reactjs - 使用 redux thunk 测试异步操作,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40165564/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com