【问题标题】:How to test pending flag in Angular如何在 Angular 中测试挂起的标志
【发布时间】:2021-03-21 20:26:24
【问题描述】:

我有 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);
});

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

【问题讨论】:

  • fetchBooks 方法是组件方法吗?请提供完整、最少的代码。
  • 是的,它是组件方法!我已经更新了示例
  • @bubbles 感谢分享这篇文章,但我不认为它解释了如何测试挂起状态。

标签: angular typescript rxjs jasmine angular-test


【解决方案1】:

我的解决方案是使用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);
});

【讨论】:

    【解决方案2】:

    我会这样做:

    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) 没有返回 observable,所以 this.isLoading = false 不会被遍历,但函数的开头会这样做 this.isLoading = true 会。所以基本上这是测试函数的前半部分是否良好,本质上 this.isLoading = true 发生在可观察的“解析”或发出之前。

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

    【讨论】:

    • 很抱歉,您在哪里测试 isLoadingtrue 但后来切换到 false
    • 请在您的代码块外部解释您如何尝试实现所提出的问题。
    • rel1x:我将一个测试一分为二以使其更容易。要通过一项测试完成您想要的操作,需要向可观察对象添加延迟或弄乱时间,这可能会变得复杂。 @Roy 我添加了一个编辑。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-03-31
    • 2014-04-15
    • 2013-04-12
    • 1970-01-01
    • 1970-01-01
    • 2022-10-04
    • 2012-02-26
    相关资源
    最近更新 更多