Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/javascript/458.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript 如何对这个Redux thunk进行单元测试?_Javascript_Redux_Chai - Fatal编程技术网

Javascript 如何对这个Redux thunk进行单元测试?

Javascript 如何对这个Redux thunk进行单元测试?,javascript,redux,chai,Javascript,Redux,Chai,因此,我有一个Redux操作创建者,它使用Redux thunk中间件: accountDetailsActions.js: export function updateProduct(product) { return (dispatch, getState) => { const { accountDetails } = getState(); dispatch({ type: types.UPDATE_PRODUCT, stateOfRe

因此,我有一个Redux操作创建者,它使用
Redux thunk
中间件:

accountDetailsActions.js:

export function updateProduct(product) {
  return (dispatch, getState) => {
    const { accountDetails } = getState();

    dispatch({
      type: types.UPDATE_PRODUCT,
      stateOfResidence: accountDetails.stateOfResidence,
      product,
    });
  };
}
describe('types.UPDATE_PRODUCT', () => {
    it('should update product when passed a product object', () => {
        //arrange
        const initialState = {
            product: {}
        };
        const product = {
            id: 1,
            accountTypeId: 1,
            officeRangeId: 1,
            additionalInfo: "",
            enabled: true
        };
        const action = actions.updateProduct(product);
        const store = mockStore({courses: []}, action);
        store.dispatch(action);
        //this is as far as I've gotten - how can I populate my newState variable in order to test the `product` field after running the thunk?
        //act
        const newState = accountDetailsReducer(initialState, action);
        //assert
        expect(newState.product).to.be.an('object');
        expect(newState.product).to.equal(product);
    });
});
如何测试它?我正在使用
chai
包进行测试。我在网上找到了一些资源,但不确定如何继续。以下是我目前的测试:

accountDetailsReducer.test.js:

export function updateProduct(product) {
  return (dispatch, getState) => {
    const { accountDetails } = getState();

    dispatch({
      type: types.UPDATE_PRODUCT,
      stateOfResidence: accountDetails.stateOfResidence,
      product,
    });
  };
}
describe('types.UPDATE_PRODUCT', () => {
    it('should update product when passed a product object', () => {
        //arrange
        const initialState = {
            product: {}
        };
        const product = {
            id: 1,
            accountTypeId: 1,
            officeRangeId: 1,
            additionalInfo: "",
            enabled: true
        };
        const action = actions.updateProduct(product);
        const store = mockStore({courses: []}, action);
        store.dispatch(action);
        //this is as far as I've gotten - how can I populate my newState variable in order to test the `product` field after running the thunk?
        //act
        const newState = accountDetailsReducer(initialState, action);
        //assert
        expect(newState.product).to.be.an('object');
        expect(newState.product).to.equal(product);
    });
});
我的thunk不执行任何异步操作。有什么建议吗?

看看官方文件。另外,你在测试什么,动作创建者还是减速器

ActionCreator测试示例 减速器测试示例 您的reducer应该是一个纯函数,因此您可以在存储环境之外单独测试它

const yourReducer = require('../reducers/your-reducer');

describe('reducer test', () => {
    it('should do things', () => {
        const initialState = {
            product: {}
        };

        const action = {
            type: types.UPDATE_PRODUCT,
            stateOfResidence: // whatever values you want to test with,
            product: {
                id: 1,
                accountTypeId: 1,
                officeRangeId: 1,
                additionalInfo: "",
                enabled: true
            }
        }

        const nextState = yourReducer(initialState, action);

        expect(nextState).to.be.eql({ /* ... */ });
    });
});

如何单元测试Redux Thunks

thunk动作创建者的全部目的是在将来分派异步动作。当使用redux thunk时,一个好的方法是对开始和结束的异步流进行建模,从而通过三个操作获得成功或错误

尽管本例使用Mocha和Chai进行测试,但您也可以很容易地使用任何断言库或测试框架

使用我们的主要thunk action创建者管理的多个操作对异步流程进行建模

为了这个例子,让我们假设您想要执行一个异步操作来更新产品,并且想要知道三件关键的事情

  • 异步操作开始时
  • 异步操作完成时
  • 异步操作是否成功或失败
好的,现在是时候根据操作生命周期的这些阶段来建模我们的redux操作了。记住,这同样适用于所有异步操作,因此这通常适用于从api获取数据的http请求

我们可以这样写我们的行为

accountDetailsActions.js:

请注意底部的忙视操作。这是我们的thunk action创造者。因为它返回一个函数,所以它是一个特殊的操作,被redux thunk中间件截获。thunk动作创建者可以在将来的某个时间点分派其他动作创建者。很聪明

现在,我们已经编写了一些操作来模拟一个异步进程,这是一个用户更新。假设这个过程是一个函数调用,它返回一个承诺,这将是当今处理异步过程最常用的方法

为实际异步操作定义逻辑,我们正在使用redux操作建模该操作

对于本例,我们将只创建一个返回承诺的泛型函数。将其替换为更新用户或执行异步逻辑的实际函数。确保函数返回一个承诺

我们将使用下面定义的函数来创建一个独立的示例。要获得一个工作示例,只需将此函数放入您的操作文件中,使其位于thunk操作创建者的范围内

 // This is only an example to create asynchronism and record time taken
 function updateUser(){
      return new Promise( // Returns a promise will be fulfilled after a random interval
          function(resolve, reject) {
              window.setTimeout(
                  function() {
                      // We fulfill the promise with the time taken to fulfill
                      resolve(thisPromiseCount);
                  }, Math.random() * 2000 + 1000);
          }
      )
})
我们的测试文件

import configureMockStore from 'redux-mock-store'
import thunk from 'redux-thunk'
import chai from 'chai' // You can use any testing library
let expect = chai.expect;

import { updateProduct } from './accountDetailsActions.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 = [ 
        'updateProductStarted', 
        'updateProductSuccessful'
    ]

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

  })

  it('expected actions should be dispatched on failed request', () => {
    const store = mockStore({})
    const expectedActions = [ 
        'updateProductStarted', 
        'updateProductFailure'
    ]

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

  })
})

在Reducer测试示例中,您手动创建action对象,而在我的测试中,我从action creator方法(
actions.updateProduct()
)获取操作。在测试减缩器时,哪种方法是更好的实践?我认为这取决于动作创建者的复杂性。如果它相当复杂(例如thunks),我建议创建如上所述的操作来关注减速器逻辑。当您的动作创建者仅仅返回一个对象时,我会按照您的建议内联创建它们。我总是建议使用最简单的解决方案,在思考事情是如何发生的方面不需要太多的开销(例如动作)。看看这个答案,它的结构非常好,但它不能测试动作创建者是否使用正确的参数被调用,对吗?你能看看这个吗?不会调用
fetchMock.restore()
,因为您正在
store.dispatch()
上执行
return
?我刚刚在这里介绍了使用redux thunk测试操作的方法,就像在主题中一样。您好,请始终尝试解释您将来演示的任何代码。欢迎来到StackOverflow!
export const someAsyncAction = (param) => (dispatch, getState) => {
    const { mock } = getState();
    dispatch({
        type: 'SOME_TYPE',
        mock: mock + param,
    })
}

it('should test someAsyncAction', () => {
    const param = ' something';
    const dispatch = jest.fn().mockImplementation();
    const getState = () => ({
        mock: 'mock value',
    });

    const expectedAction = {
        type: 'SOME_TYPE',
        mock: 'mock value something'
    };

    const callback = someAsyncAction(param);
    expect(typeof callback).toBe('function');

    callback.call(this, dispatch, getState);
    expect(dispatch.mock.calls[0]).toEqual([expectedAction])
});