如何使用茉莉花大理石测试forkJoin()运算符

时间:2019-01-24 20:21:49

标签: angular unit-testing jasmine rxjs 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();
    });
  });
});

测试没有附件的表单数据成功,但是测试有附件的表单数据失败。

来自失败的错误消息:

  

✖提交附件   无头Chrome 71.0.3578(Mac OS X 10.14.2)   错误:预期的$ .length = 0等于2。   预期的$ [0] =未定义为等于Object({帧:10,通知:Notification({种类:'N',值:['令牌'],错误:未定义,hasValue:true})})。   预期的$ [1] =未定义为相等的Object({帧:40,通知:Notification({种类:'N',值:Object({id:'id_123'}),错误:未定义,hasValue:true})} )。

似乎output在失败的测试中无法观察到,undefined却是可见的,但问题是-为什么? 因为在另一个测试中,它在不发送附件并使用forkJoin()时发出了它。

任何人都知道为什么会这样吗?谢谢!

1 个答案:

答案 0 :(得分:0)

解决了此问题,问题是第一个可观察到的问题是从http.post调用-cold('-a-', { a: [{ attachment_token: 'token' }] }),返回的。它没有发出新的可观察到的信号,因此所有测试都停止了。将其更改为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);
})