手动将值传递给生成器会在玩笑测试中产生意想不到的结果
Manually passing a value to a generator yields unexpected results with jest tests
我有以下 redux 传奇,它进行 api 调用并处理结果:
import { takeLatest } from 'redux-saga'
import { call, put } from 'redux-saga/effects'
import { normalize, arrayOf } from 'normalizr'
import * as actions from './actions'
import * as schemas from './schemas'
import * as types from './actionTypes'
import { api, constants as apiConstants } from '../../services/api'
export function* fetchWorks() {
const { response, error } = yield call(api.getJson, apiConstants.WORKS_ENDPOINT)
if (response) {
const normalized = normalize(response.items, arrayOf(schemas.works))
yield put(actions.fetchWorksSuccess(normalized))
} else {
yield put(actions.fetchWorksFail(error))
}
}
我正在测试这些玩笑测试是否一切正常:
import { takeLatest } from 'redux-saga'
import { call, put } from 'redux-saga/effects'
import * as sagas from './sagas'
import * as actions from './actions'
import * as types from './actionTypes'
import { api, constants as apiConstants } from '../../services/api'
describe('works saga', () => {
describe('fetchWorks', () => {
it('should fetch data', () => {
const generator = sagas.fetchWorks()
const actual = generator.next().value
const expected = call(api.getJson, apiConstants.WORKS_ENDPOINT)
expect(actual).toEqual(expected)
})
// this test requires me to pass a response object to the generator
it('should put fetchWorksSuccess on a response', () => {
// so I create it here
const response = {
items: [{
sys: { id: '1' },
fields: { content: 'content' }
}]
}
const generator = sagas.fetchWorks()
const expected = put(actions.fetchWorksSuccess())
// but I would expect to pass it here
generator.next()
// but actually the test only succeeds if I pass it here
const actual = generator.next({ response }).value
expect(actual).toEqual(expected)
})
// same goes for this test
it('should put fetchWorksFail on errors', () => {
const error = new Error('Something went wrong')
const generator = sagas.fetchWorks()
const expected = put(actions.fetchWorksFail())
generator.next()
const actual = generator.next({ error }).value
expect(actual).toEqual(expected)
})
})
})
但是,对于 'should put fetchWorksSuccess on a response'
和 'should put fetchWorksFail on errors'
测试,我必须分别手动将 {response}
和 {error}
对象传递给每个生成器。
我知道这些对象是必需的(因为 if 语句检查是否有响应),但我不明白为什么我必须将它传递给第二个 .next()
而不是第一的?因为我看到它的方式是第一个 yield
产生响应或错误对象,而不是第二个。有人明白为什么吗?
这不是 React 的东西,它是生成器的东西。
第一次调用 generator.next()
启动生成器,而不是来自 yield 调用的 returns:
>>> function *example_generator() {
console.log('before first yield', Array.slice(arguments));
var first_yield = yield 'first';
console.log('first yield returned', first_yield);
var second_yield = yield 'second';
console.log('second yield returned', second_yield);
return 'done';
}
>>> generator = example_generator("generator", "creation")
Generator {}
>>> generator.next("first next")
before first yield Array [ "generator", "creation" ]
Object { value: "first", done: false }
>>> generator.next("second next")
first yield returned second next
Object { value: "second", done: false }
>>> generator.next("third next")
second yield returned third next
Object { value: "done", done: true }
您对 fetchWorks()
的初始调用会创建生成器对象,但实际上并未启动生成器。
这样想——第一次调用 next()
给你传递给第一个 yield
的值;第二次调用 next()
的参数允许您指定第一个 yield
.
的 return 值
我有以下 redux 传奇,它进行 api 调用并处理结果:
import { takeLatest } from 'redux-saga'
import { call, put } from 'redux-saga/effects'
import { normalize, arrayOf } from 'normalizr'
import * as actions from './actions'
import * as schemas from './schemas'
import * as types from './actionTypes'
import { api, constants as apiConstants } from '../../services/api'
export function* fetchWorks() {
const { response, error } = yield call(api.getJson, apiConstants.WORKS_ENDPOINT)
if (response) {
const normalized = normalize(response.items, arrayOf(schemas.works))
yield put(actions.fetchWorksSuccess(normalized))
} else {
yield put(actions.fetchWorksFail(error))
}
}
我正在测试这些玩笑测试是否一切正常:
import { takeLatest } from 'redux-saga'
import { call, put } from 'redux-saga/effects'
import * as sagas from './sagas'
import * as actions from './actions'
import * as types from './actionTypes'
import { api, constants as apiConstants } from '../../services/api'
describe('works saga', () => {
describe('fetchWorks', () => {
it('should fetch data', () => {
const generator = sagas.fetchWorks()
const actual = generator.next().value
const expected = call(api.getJson, apiConstants.WORKS_ENDPOINT)
expect(actual).toEqual(expected)
})
// this test requires me to pass a response object to the generator
it('should put fetchWorksSuccess on a response', () => {
// so I create it here
const response = {
items: [{
sys: { id: '1' },
fields: { content: 'content' }
}]
}
const generator = sagas.fetchWorks()
const expected = put(actions.fetchWorksSuccess())
// but I would expect to pass it here
generator.next()
// but actually the test only succeeds if I pass it here
const actual = generator.next({ response }).value
expect(actual).toEqual(expected)
})
// same goes for this test
it('should put fetchWorksFail on errors', () => {
const error = new Error('Something went wrong')
const generator = sagas.fetchWorks()
const expected = put(actions.fetchWorksFail())
generator.next()
const actual = generator.next({ error }).value
expect(actual).toEqual(expected)
})
})
})
但是,对于 'should put fetchWorksSuccess on a response'
和 'should put fetchWorksFail on errors'
测试,我必须分别手动将 {response}
和 {error}
对象传递给每个生成器。
我知道这些对象是必需的(因为 if 语句检查是否有响应),但我不明白为什么我必须将它传递给第二个 .next()
而不是第一的?因为我看到它的方式是第一个 yield
产生响应或错误对象,而不是第二个。有人明白为什么吗?
这不是 React 的东西,它是生成器的东西。
第一次调用 generator.next()
启动生成器,而不是来自 yield 调用的 returns:
>>> function *example_generator() {
console.log('before first yield', Array.slice(arguments));
var first_yield = yield 'first';
console.log('first yield returned', first_yield);
var second_yield = yield 'second';
console.log('second yield returned', second_yield);
return 'done';
}
>>> generator = example_generator("generator", "creation")
Generator {}
>>> generator.next("first next")
before first yield Array [ "generator", "creation" ]
Object { value: "first", done: false }
>>> generator.next("second next")
first yield returned second next
Object { value: "second", done: false }
>>> generator.next("third next")
second yield returned third next
Object { value: "done", done: true }
您对 fetchWorks()
的初始调用会创建生成器对象,但实际上并未启动生成器。
这样想——第一次调用 next()
给你传递给第一个 yield
的值;第二次调用 next()
的参数允许您指定第一个 yield
.