2016-09-10 3 views
0

Как проверить функцию внутри оператора if или try/catch? Например,test redux-сага внутри оператора if и с использованием реальных значений

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)) 
    } 
} 

мне нужно ввести реальное имя канала, который на самом деле существует в базе данных для того, чтобы вернуть правильный ответ для выходов, которые следуют выполнить, в противном случае он выдаст сообщение об ошибке. Кроме того, я получу сообщение об ошибке, не могу прочитать свойство json undefined, поэтому результат после этого не может быть достигнут из-за этого сообщения об ошибке. Итак, моя первая проблема - «if (response.ok)», но даже если я ее удалю, дайте response.json() вернет ошибку и, кроме того, выход после этого не будет выполнен. Если кто-нибудь может показать мне, как проверить их, будет очень признателен.

ответ

2

Передайте объект ответа на предыдущее выполнение и проверку условия, я бы сделал это так, надеюсь, что это помогает:

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); 
}); 

}); 

Обратите внимание, что ваш код, вероятно, делает больше вещей, я не в курсе, но это по крайней мере, нужно поместить u в некоторую строку, чтобы решить вашу проблему.

+0

спасибо, насмехается ответ с использованием свойства ok выглядит как план, но как бы вы это сделали? Если вы можете закодировать последний раздел, это было бы здорово (так как это часть, которая действительно имеет значение) – mtangula

+1

передать объект, например saga.next ({ok : true}). – andresmijares25

0

Возможно, вы захотите использовать вспомогательную библиотеку, например redux-saga-testing.

Отказ от ответственности: Я написал эту библиотеку, чтобы решить, что точно такая же проблема

Для вашего конкретного примера, используя шутя (но работает так же для мокко), я хотел бы сделать две вещи:

  • во-первых, я хотел бы отделить вызов API к другой функции
  • Тогда я хотел бы использовать Redux-сага-тестирование, чтобы проверить свою логику в синхронном образом:

Вот код:

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' 
    }); 
}); 

Вы найдете множество других примеров (более сложные) на project's GitHub.

0

Вот связанный с этим вопрос: в redux-saga документы, у них есть примеры, где take является прослушивание нескольких действий. Исходя из этого, я написал сагу аутентификации, который выглядит более или менее, как это (вы можете признать, что это модифицированная версия примера из 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 в этом контексте. Как это должно работать?