如何在 Angular 中测试挂起的标志

How to test pending flag in Angular

我有 fetchBooks 从后端检索数据然后填充 books 变量的方法。

import { Component, OnInit } from "@angular/core";

@Component({
  selector: "app-root",
  templateUrl: "./app.component.html",
})
export class AppComponent implements OnInit {
  isLoading: boolean;
  books;

  constructor(private booksService: BooksService) {}

  ngOnInit() {
    this.fetchBooks();
  }

  fetchBooks(): void {
    this.isLoading = true;
    this.booksService
      .fetchBooks()
      .subscribe(
        response => {
          this.isLoading = false;
          this.books = response.data;
          // ..
        },
        error => {
          this.isLoading = false;
          // ..
        }
      );
  }
}

我需要为 isLoading 标志编写单元测试。我写了这样的东西

it('should work', async () => {
  sut.fetchBooks();

  // ...
  expect(sut.isLoading).toBe(true);
  // ...
  expect(sut.isLoading).toBe(false);
});

但我与其他人斗争。也许有人知道如何解决它或知道一些解释它的文章?

我会这样做:

import { of } from 'rxjs';
....
it('should turn loading on when fetchBooks is called', () => {
  // notice we don't resolve bookService.fetchBooks in this case so
  // it doesn't go in the subscribe callback
  sut.fetchBooks();
  expect(sut.isLoading).toBe(true);
});

it('should turn loading off when bookService.fetchBooks resolves', () => {
  // make bookService.fetchBooks return instantaneously this time.
  spyOn(bookService, 'fetchBooks').and.returnValue(of({ data: [] }));
  sut.fetchBooks();
  expect(sut.isLoading).toBe(true);
  expect(sut.books).toEqual([]); // use toEqual here because toBe uses triple equality
                                 // and [] === [] returns false because it is comparing
                                 // locations in memory
});

编辑

我将测试分成两部分以便测试更容易。

第一个测试 observable (bookService.fetchBooks) 没有 return observable 所以 this.isLoading = false 没有被遍历但是函数的开头如此 this.isLoading = true .所以基本上这是测试函数的前半部分是好的,本质上 this.isLoading = true 发生在可观察的“解析”或发出之前。

第二个测试使用 .returnValue(of({ data: [] })); 立即解析 observable,因此我们测试当 observable 发出时加载被关闭。

我的解决方案是利用Subject来控制值的发射。将 subject.asObservable() 传递给您的 SUT 订阅的间谍。

import { Subject } from 'rxjs';

it('should correctly work', () => {
  // replace SomeType with the type returned by booksService.fetchData()
  const subject = new Subject<SomeType>();
  spyOn(booksService, 'fetchData').and.returnValue(subject.asObservable());
  const mockData: SomeType = {}; // put some data here, same type;

  // bonus, you may want to check also that isLoading is initially false
  // expect(sut.isLoading).toBe(false);

  // check: after triggered, isLoading should be true until the subscription returns a value
  sut.ngOnOnit();
  expect(sut.isLoading).toBe(true);

  // check: after the subscription receives a value, isLoading should be false
  subject.next(mockData);
  expect(sut.isLoading).toBe(false);

  // bonus
  expect(sut.data).toEqual(mockData);
  expect(booksService.fetchData).toHaveBeenCalledTimes(1);
});