2

如何在 if 语句或 try/catch 中测试函数?例如,

export function* onFetchMessages(channel) {
    yield put(requestMessages())
    const channel_name = channel.payload
    try {
        const response = yield call(fetch,'/api/messages/'+channel_name)

        if(response.ok){
            const res = yield response.json();

            const date = moment().format('lll');
            yield put(receiveMessages(res,channel.payload,date))
        }


    } catch (error){
        yield put(rejectMessages(error))
    }
}

我需要输入一个实际存在于数据库中的真实通道名称,以便它为随后执行的收益返回有效响应,否则将引发错误。另外,我会得到一个错误信息,cannot read property json of undefined,所以由于这个错误信息,无法达到之后的 yield。所以我的第一个问题是 'if(response.ok)' 但即使我删除它,yield response.json() 也会返回一个错误,而且之后的 yield 也不会被执行。如果有人能告诉我如何测试这些,将不胜感激。

4

3 回答 3

3

将响应对象传递给之前的执行和测试条件,我会这样做,希望这会有所帮助:

 export function* onFetchMessages(channel) {
try {
    yield put(requestMessages())
    const channel_name = channel.payload
    const response = yield call(fetch,'/api/messages/'+channel_name)

    if(response.ok){
        const res = yield response.json();

        const date = moment().format('lll');
        yield put(receiveMessages(res,channel.payload,date))
    }

   } catch (error){
      yield put(rejectMessages(error))
  }
}

describe('onFetchMessages Saga', () => {
 let output = null;
 const saga = onFetchMessages(channel); //mock channel somewhere...

 it('should put request messages', () => {
  output = saga.next().value;
  let expected = put(requestMessages()); //make sure you import this dependency
  expect(output).toEqual(expected);
 });

 it('should call fetch...blabla', ()=> {
  output = saga.next(channel_name).value; //include channel_name so it is avaiable on the next iteration
  let expected = call(fetch,'/api/messages/'+channel_name); //do all the mock you ned for this
  expect(output).toEqual(expected);
 });

 /*here comes you answer*/
 it('should take response.ok into the if statemenet', ()=> {
  //your json yield is out the redux-saga context so I dont assert it
   saga.next(response).value; //same as before, mock it with a ok property, so it is available
   output = saga.next(res).value; //assert the put effect
   let expected = put(receiveMessages(res,channel.payload,date)); //channel should be mock from previous test
   expect(output).toEqual(expected);
 });

});

请注意,您的代码可能会做更多我不知道的事情,但这至少应该让您解决您的问题。

于 2016-09-11T16:31:02.390 回答
1

您可能希望为此使用帮助程序库,例如redux-saga-testing

免责声明:我编写了这个库来解决完全相同的问题

对于您的具体示例,使用 Jest(但对 Mocha 的工作方式相同),我会做两件事:

  • 首先,我会将 API 调用分离到不同的函数
  • 然后我会使用 redux-saga-testing 以同步方式测试你的逻辑:

这是代码:

import sagaHelper from 'redux-saga-testing';
import { call, put } from 'redux-saga/effects';
import { requestMessages, receiveMessages, rejectMessages } from './my-actions';

const api = url => fetch(url).then(response => {
    if (response.ok) {
        return response.json();
    } else {
        throw new Error(response.status); // for example
    }
});

function* onFetchMessages(channel) {
    try {
        yield put(requestMessages())
        const channel_name = channel.payload
        const res = yield call(api, '/api/messages/'+channel_name)
        const date = moment().format('lll');

        yield put(receiveMessages(res,channel.payload,date))
    } catch (error){
        yield put(rejectMessages(error))
    }
}


describe('When testing a Saga that throws an error', () => {
    const it = sagaHelper(onFetchMessages({ type: 'foo', payload: 'chan1'}));

    it('should have called the API first, which will throw an exception', result => {
        expect(result).toEqual(call(api, '/api/messages/chan1'));
        return new Error('Something went wrong');
    });

    it('and then trigger an error action with the error message', result => {
        expect(result).toEqual(put(rejectMessages('Something went wrong')));
    });
});

describe('When testing a Saga and it works fine', () => {
    const it = sagaHelper(onFetchMessages({ type: 'foo', payload: 'chan2'}));

    it('should have called the API first, which will return some data', result => {
        expect(result).toEqual(call(api, '/api/messages/chan2'));
        return 'some data';
    });

    it('and then call the success action with the data returned by the API', result => {
        expect(result).toEqual(put(receiveMessages('some data', 'chan2', 'some date')));
        // you'll have to find a way to mock the date here'
    });
});

您会在项目的 GitHub 上找到许多其他示例(更复杂的示例)。

于 2016-10-19T08:22:26.307 回答
0

这是一个相关的问题:在redux-saga文档中,他们有take监听多个动作的例子。基于此,我编写了一个看起来或多或少像这样的 auth saga(您可能会认识到这是redux-saga文档中示例的修改版本:

function* mySaga() { 
    while (true) {
        const initialAction = yield take (['AUTH__LOGIN','AUTH__LOGOUT']);
        if (initialAction.type === 'AUTH__LOGIN') {
            const authTask = yield fork(doLogin);
            const action = yield take(['AUTH__LOGOUT', 'AUTH__LOGIN_FAIL']);
            if (action.type === 'AUTH__LOGOUT') {
                yield cancel(authTask);
                yield call (unauthorizeWithRemoteServer)
            }
        } else {
            yield call (unauthorizeWithRemoteServer)
        }
    }
}

在处理 Sagas 时,我不认为这是一种反模式,并且代码肯定会在测试环境(Jest)之外按预期运行。但是,我看不到在这种情况下处理 if 语句的方法。这应该如何工作?

于 2016-11-22T15:41:39.267 回答