如何用 jasmine-marbles 测试 forkJoin() 运算符
How to test forkJoin() operator with jasmine-marbles
我创建了向后端发送数据的服务,数据由用户在 UI 中填写。用户还可以上传任何文件以将其发送到后端。我正在尝试用茉莉花弹珠测试此功能。
这是我的服务代码:
export class FormSubmitService {
constructor(private http: HttpClient) {}
public submit(data, attachments) {
const observables = [
...attachments.map((file) => this.uploadAttachment(file)),
];
return forkJoin(...observables)
.pipe(
defaultIfEmpty([]),
map((tokens) => {
return tokens.map((tokenObj) => tokenObj.attachment_token);
}),
switchMap((tokens: string[]) => {
const formData = {
data,
attachments: tokens,
};
return this.submitForm(formData);
}),
);
}
private uploadAttachment(attachment: File) {
const attachmentData: FormData = new FormData();
attachmentData.append('attachment', attachment, attachment.name);
return this.http.post(
'/some/form/endpoint/attachments',
attachmentData,
);
}
private submitForm(userData: UserData) {
return this.http.post(
`/some/form/endpoint/form`,
userData,
);
}
}
如果用户添加一个或多个附件,那么在我将用户数据发送到后端之前,我需要将每个附件上传到后端以获取每个附件的令牌,稍后我将其存储在数组中。我正在使用 forkJoin()
进行操作,等待所有附件上传完毕,然后使用 switchMap
提交用户数据。
这是我的两个测试用例(一个工作,一个不工作):
describe('SubmitFormData', () => {
let service: FormSubmitService;
let http: jasmine.SpyObj<HttpClient>;
beforeEach(() => {
http = jasmine.createSpyObj('HttpClient', ['post']);
service = new FormSubmitService(http);
});
describe('#submit', () => {
const file = new File([''], 'filename', { type: 'image/png' });
const attachments = [file];
const data = {
name: 'name',
description: 'description',
};
// NOT WORKING!
it('submit with attachment', () => {
const expected = cold('-a--b-', { a: ['token'], b: { id: 'id_123' } }); // FAIL!
// const expected = cold('----'); // SUCCESS!
http.post.and.returnValues(
cold('-a-', { a: [{ attachment_token: 'token' }] }),
cold('-a-', { a: { id: 'id_123' } }),
);
const output = service.submit(data, attachments);
expect(output).toBeObservable(expected);
expect(http.post).toHaveBeenCalled();
});
// WORKING!
it('submit without attachment', () => {
const response = {
id: 'id_123',
};
const expected = cold('-a-', { a: response });
http.post.and.returnValues(
cold('-a-', { a: { id: 'id_123' } }),
);
const output = service.submit(data, []);
expect(output).toBeObservable(expected);
expect(http.post).toHaveBeenCalled();
});
});
});
测试无附件的表单数据成功,但测试有附件的表单数据失败。
失败的错误消息:
✖ submit with attachment
HeadlessChrome 71.0.3578 (Mac OS X 10.14.2)
Error: Expected $.length = 0 to equal 2.
Expected $[0] = undefined to equal Object({ frame: 10, notification: Notification({ kind: 'N', value: [ 'token' ], error: undefined, hasValue: true }) }).
Expected $[1] = undefined to equal Object({ frame: 40, notification: Notification({ kind: 'N', value: Object({ id: 'id_123' }), error: undefined, hasValue: true }) }).
似乎 output
在失败的测试中没有发出 observable 而是 undefined
,但问题是 - 为什么?
因为在另一个测试中,它在不发送附件并使用 forkJoin()
.
时发出它
有人知道为什么会这样吗?谢谢!
修复了这个问题,问题出在从 http.post
调用 - cold('-a-', { a: [{ attachment_token: 'token' }] }),
返回的第一个 observable 上。它没有发出新的可观察值,此时所有测试都停止了。改成of({ attachment_token: 'token' }),
,测试成功
这是一个代码:
it('submit with attachment', () => {
const response = {
id: 'id_123',
};
http.post.and.returnValues(
of({ attachment_token: 'token' }),
cold('-a', { a: response }),
);
const expected = cold('-a', { a: response });
const output = service.submit(data, attachments);
expect(output).toBeObservable(expected);
expect(http.post).toHaveBeenCalledTimes(2);
})
我创建了向后端发送数据的服务,数据由用户在 UI 中填写。用户还可以上传任何文件以将其发送到后端。我正在尝试用茉莉花弹珠测试此功能。
这是我的服务代码:
export class FormSubmitService {
constructor(private http: HttpClient) {}
public submit(data, attachments) {
const observables = [
...attachments.map((file) => this.uploadAttachment(file)),
];
return forkJoin(...observables)
.pipe(
defaultIfEmpty([]),
map((tokens) => {
return tokens.map((tokenObj) => tokenObj.attachment_token);
}),
switchMap((tokens: string[]) => {
const formData = {
data,
attachments: tokens,
};
return this.submitForm(formData);
}),
);
}
private uploadAttachment(attachment: File) {
const attachmentData: FormData = new FormData();
attachmentData.append('attachment', attachment, attachment.name);
return this.http.post(
'/some/form/endpoint/attachments',
attachmentData,
);
}
private submitForm(userData: UserData) {
return this.http.post(
`/some/form/endpoint/form`,
userData,
);
}
}
如果用户添加一个或多个附件,那么在我将用户数据发送到后端之前,我需要将每个附件上传到后端以获取每个附件的令牌,稍后我将其存储在数组中。我正在使用 forkJoin()
进行操作,等待所有附件上传完毕,然后使用 switchMap
提交用户数据。
这是我的两个测试用例(一个工作,一个不工作):
describe('SubmitFormData', () => {
let service: FormSubmitService;
let http: jasmine.SpyObj<HttpClient>;
beforeEach(() => {
http = jasmine.createSpyObj('HttpClient', ['post']);
service = new FormSubmitService(http);
});
describe('#submit', () => {
const file = new File([''], 'filename', { type: 'image/png' });
const attachments = [file];
const data = {
name: 'name',
description: 'description',
};
// NOT WORKING!
it('submit with attachment', () => {
const expected = cold('-a--b-', { a: ['token'], b: { id: 'id_123' } }); // FAIL!
// const expected = cold('----'); // SUCCESS!
http.post.and.returnValues(
cold('-a-', { a: [{ attachment_token: 'token' }] }),
cold('-a-', { a: { id: 'id_123' } }),
);
const output = service.submit(data, attachments);
expect(output).toBeObservable(expected);
expect(http.post).toHaveBeenCalled();
});
// WORKING!
it('submit without attachment', () => {
const response = {
id: 'id_123',
};
const expected = cold('-a-', { a: response });
http.post.and.returnValues(
cold('-a-', { a: { id: 'id_123' } }),
);
const output = service.submit(data, []);
expect(output).toBeObservable(expected);
expect(http.post).toHaveBeenCalled();
});
});
});
测试无附件的表单数据成功,但测试有附件的表单数据失败。
失败的错误消息:
✖ submit with attachment HeadlessChrome 71.0.3578 (Mac OS X 10.14.2) Error: Expected $.length = 0 to equal 2. Expected $[0] = undefined to equal Object({ frame: 10, notification: Notification({ kind: 'N', value: [ 'token' ], error: undefined, hasValue: true }) }). Expected $[1] = undefined to equal Object({ frame: 40, notification: Notification({ kind: 'N', value: Object({ id: 'id_123' }), error: undefined, hasValue: true }) }).
似乎 output
在失败的测试中没有发出 observable 而是 undefined
,但问题是 - 为什么?
因为在另一个测试中,它在不发送附件并使用 forkJoin()
.
有人知道为什么会这样吗?谢谢!
修复了这个问题,问题出在从 http.post
调用 - cold('-a-', { a: [{ attachment_token: 'token' }] }),
返回的第一个 observable 上。它没有发出新的可观察值,此时所有测试都停止了。改成of({ attachment_token: 'token' }),
,测试成功
这是一个代码:
it('submit with attachment', () => {
const response = {
id: 'id_123',
};
http.post.and.returnValues(
of({ attachment_token: 'token' }),
cold('-a', { a: response }),
);
const expected = cold('-a', { a: response });
const output = service.submit(data, attachments);
expect(output).toBeObservable(expected);
expect(http.post).toHaveBeenCalledTimes(2);
})