Vla*_*nov 6 form-data reactjs jestjs redux axios
我正在使用 jest+nock+jsdom 模块来测试我的 React\Redux 应用程序。我需要测试这个异步操作函数:
export function updateUserPhoto (file, token) {
const data = new FormData()
data.append('file', file)
return dispatch => {
dispatch(userPutPhotoRequest())
return axios({
method: 'PUT',
headers: {
'x-access-token': token
},
data: data,
url: API_URL + '/user/photo'
})
.then(res => dispatch(userPutPhotoSuccess(res.data)))
.catch(err => dispatch(userPutPhotoFilure(err)))
}
}
Run Code Online (Sandbox Code Playgroud)
所以我使用 jsdom 向测试提供 FormData 和 File 对象:
const {JSDOM} = require('jsdom')
const jsdom = (new JSDOM(''))
global.window = jsdom.window
global.document = jsdom.window.document
global.FormData = jsdom.window.FormData
const File = jsdom.window.File
global.File = jsdom.window.File
Run Code Online (Sandbox Code Playgroud)
这是测试“上传照片”功能的方法:
it('creates USER_UPDATE_SUCCESS when updating user photo has been done', () => {
const store = mockStore(Map())
const file = new File([''], 'filename.txt', {
type: 'text/plain',
lastModified: new Date()
})
const expectedFormData = new FormData()
expectedFormData.append('file', file)
nock(API_URL, {
reqheaders: {
'x-access-token': token
}
}).put('/user/photo', expectedFormData)
.reply(200, {body: {}})
const expectedActions = [
{
type: ActionTypes.USER_PUT_PHOTO_REQUEST
},
{
type: ActionTypes.USER_PUT_PHOTO_SUCCESS,
response: {
body: {}
}
}
]
return store.dispatch(actions.updateUserPhoto(file, token))
.then(() => {
// return of async actions
expect(store.getActions()).toEqual(expectedActions)
})
})
Run Code Online (Sandbox Code Playgroud)
我使用 nock 来模拟 axios 请求,使用 redux-mock-store 来模拟 Redux 存储。创建 File 和 FormData 对象以将其与 axios 的响应进行比较。然后我调用操作函数,传递文件和令牌作为参数。
生产动作功能正常,调度动作成功。但在测试中我收到错误:
Error: Data after transformation must be a string, an ArrayBuffer, a Buffer, or a Stream
Run Code Online (Sandbox Code Playgroud)
当我传递到 axios 空对象作为数据测试通过时,所以 FormData 对象出现问题。我如何以适当的方式模拟 axios 的 FormData 对象以使此测试工作?
这个答案来得太晚了,但我想做类似的事情,我想在这里发布一个解决方案,其他人可能会偶然发现并发现有用。
这里的主要问题是 nock 模拟网络请求而不是 Javascript 库。 FormData是一个 Javascript 对象,在发出网络请求时最终会转换为文本。当FormData对象进行 nock 时,它已转换为 astring或 a Buffer,因此您会看到错误。nock 无法使用该FormData对象进行比较。
您有几个选择:
只是不与请求中的数据匹配PUT。您进行嘲笑的原因是因为您不希望发出真正的 HTTP 请求,但希望返回虚假响应。nock仅模拟该请求一次,因此如果您模拟所有PUT对 nock 的请求/user/photo都会捕获它,但仅限于该测试:
nock(API_URL, {
reqheaders: {
'x-access-token': token
}
}).put('/user/photo')
.reply(200, {body: {}})
Run Code Online (Sandbox Code Playgroud)
在以这种方式实施测试之前,请考虑您的测试试图验证什么。您是否尝试验证文件是否已在 HTTP 请求中发送?如果是,那么这是一个糟糕的选择。您的代码可以发送与发送的文件完全不同的文件,但仍然通过此测试。但是,如果您有另一个测试来验证文件是否正确放入 HTTP 请求中,那么此解决方案可能会节省您一些时间。
如果您确实希望在您的代码通过了损坏或错误的文件时测试失败,那么最简单的解决方案是测试文件名。由于您的文件是空的,因此不需要匹配内容,但我们可以匹配文件名:
nock(API_URL, {
reqheaders: {
'x-access-token': token
}
}).put('/user/photo', /Content-Disposition\s*:\s*form-data\s*;\s*name="file"\s*;\s*filename="filename.txt"/i)
.reply(200, {body: {}})
Run Code Online (Sandbox Code Playgroud)
这应该符合上传一个文件的简单情况。
假设您有其他字段要添加到您的请求中
export function updateUserPhoto (file, tags, token) {
const data = new FormData()
data.append('file', file)
data.append('tags', tags)
...
Run Code Online (Sandbox Code Playgroud)
或者您想要匹配的文件中有虚假内容
const file = new File(Array.from('file contents'), 'filename.txt', {
type: 'text/plain',
lastModified: new Date()
})
Run Code Online (Sandbox Code Playgroud)
这就是事情变得有点复杂的地方。本质上,您需要做的是将表单数据文本解析回对象,然后编写您自己的匹配逻辑。
parse-multipart-data是一个相当简单的解析器,您可以使用:
https://www.npmjs.com/package/parse-multipart-data
使用该包您的测试可能看起来像这样
it('creates USER_UPDATE_SUCCESS when updating user photo has been done', () => {
const store = mockStore(Map())
const file = new File(Array.from('file content'), 'filename.txt', {
type: 'text/plain',
lastModified: new Date()
})
nock(API_URL, {
reqheaders: {
'x-access-token': token
}
}).put('/user/photo', function (body) { /* You cannot use a fat-arrow function since we need to access the request headers */
// Multipart Data has a 'boundary' that works as a delimiter.
// You need to extract that
const boundary = this.headers['content-disposition']
.match(/boundary="([^"]+)"/)[1];
const parts = multipart.Parse(Buffer.from(body),boundary);
// return true to indicate a match
return parts[0].filename === 'filename.txt'
&& parts[0].type === 'text/plain'
&& parts[0].data.toString('utf8') === 'file contents'
&& parts[1].name === 'tags[]'
&& parts[1].data.toString('utf8') === 'tag1'
&& parts[2].name === 'tags[]'
&& parts[2].data.toString('utf8') === 'tag2';
})
.reply(200, {body: {}})
const expectedActions = [
{
type: ActionTypes.USER_PUT_PHOTO_REQUEST
},
{
type: ActionTypes.USER_PUT_PHOTO_SUCCESS,
response: {
body: {}
}
}
]
return store.dispatch(actions.updateUserPhoto(file, ['tag1', 'tag2'], token))
.then(() => {
// return of async actions
expect(store.getActions()).toEqual(expectedActions)
})
})
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
5357 次 |
| 最近记录: |