gpt4 book ai didi

Angular 单元测试 : how to use marble testing (rxjs/testing) to test this state management service

转载 作者:行者123 更新时间:2023-12-04 00:02:03 27 4
gpt4 key购买 nike

在我的 Angular 项目中,我有一个服务,用于状态管理以在组件之间共享一些数据,如下所示:

@Injectable({ providedIn: "root"})
export class NameStateService {

private _filteredNames$: Subject<Name[]> = new Subject();
private _filteredNamesObs$: Observable<Name[]>;

constructor() {
this._filteredNamesObs$ = this._filteredNames$.asObservable();
}

public updateFilteredNames(val: Name[]): void {
this._filteredNames$.next(val);
}

public get filteredNames$(): Observable<BillingAccount[]> {
return this._filteredNamesObs$;
}
}

状态管理基于 subject 和 observable,这是 rxjs 世界中的典型用法。

对于此服务的单元测试,我想使用 rxjs/testing 支持的弹珠测试功能。模块。解决方案如下:
describe("Name state service ", () => {
let nameStateService: NameStateService;
let scheduler: TestScheduler;

beforeEach(() => {
TestBed.configureTestingModule({
providers: [
NameStateService
]
});
nameStateService = TestBed.get(NameStateService);
scheduler = new TestScheduler((actual, expected) => expect(actual).toEqual(expected));
});

it("should be created", () => {
expect(nameStateService).toBeTruthy();
});

it("should return a valid names observables", () => {
scheduler.run(({ cold, hot, expectObservable }) => {
const mockNames: Name[] = [{
title: "title1",
group: "group1"
}];
nameStateService.updateFilteredNames(mockNames);
expectObservable(nameStateService.filteredNames$).toBe("-b", {b: mockNames});
});
});
})

但是第二个单元测试失败并出现错误: Error: Expected $.length = 0 to equal 1.所以这意味着 nameStateService.filteredNames$ ,这个 observable 在它的流中没有值。

这里有什么问题?

最佳答案

您的设置可能很难使用 RxJS 弹珠进行测试。

第一个问题是您在订阅之前将一些内容发送到流中。 Remember , expectObservable()同步订阅传递的可观察对象(在您的情况下为 nameStateService.filteredNames$ )。但是,没有数据发送到那里,因为您已经通过调用 nameStateService.updateFilteredNames(mockNames) 发送了数据。 .

您可以考虑让这两行更改位置,但请记住这是同步执行环境,所以这样做

expectObservable(nameStateService.filteredNames$).toBe("-b", {b: mockNames});
nameStateService.updateFilteredNames(mockNames);

expectObservable() 起也无济于事将订阅 nameStateService.filteredNames$ ,然后它会读取所有值,但是由于在此之后在一行中发送它们时没有值,因此 actual数组将为空。

为避免这种情况,您应该模拟您的 nameStateService.filteredNames$可观察的。要做到这一点,你可以做两件事,但他们都有自己的问题。因此,您设置的第二个问题是您可以制作 coldhot可观察并使用它们而不是 filteredNames$可观察的。

这可以这样实现:
nameStateService.filteredNames$ = hot('-b', { a: mockNames });

但 TS2540 出现此错误:无法分配给“filteredNames$”,因为它是只读属性。因为您没有 filteredNames$ 的二传手.如果您要添加 setter,那么这将违反您拥有 this._filteredNamesObs$ 的契约(Contract)。作为由 this._filteredNames$ 创建的私有(private)属性(property) Subject .

另一种方法是模拟 this._filteredNames$使用 Jasmine spy (这是第三个问题),但这种设置也有问题。 mock 什么?整机 nameStateService ?在这种情况下,您必须为服务的每个特定属性和功能创建 spy 。或模拟 nameStateService._filteredNames$属性(property)?或者更好, nameStateService.filteredNames$ ?但是 mock 他们会导致其他人的行为有所不同,因为并非所有人都被 mock 。

所以,我建议根本不要使用 TestScheduler,并像这样编写你的测试:

it('should return a valid names observables', () => {
const values: Name[][] = [];
expect(values).toEqual([]);
nameStateService.updateFilteredNames([{ group: 'G', title: 'Before subscribe' }]);
expect(values).toEqual([]);

nameStateService.filteredNames$.subscribe({
next(names) {
values.push(names);
}
});
expect(values).toEqual([]);

nameStateService.updateFilteredNames([{ group: 'G', title: 'After subscribe' }]);
expect(values).toEqual([[{ group: 'G', title: 'After subscribe' }]]);
});

但是,可能存在另一种解决方案(如果您真的非常想使用弹珠),您只需要非常小心并知道自己在做什么。您可以更改 _filteredNames$ReplaySubject .
private _filteredNames$: Subject<Name[]> = new ReplaySubject();

这将导致任何后续订阅者在订阅之前获取所有沿流发送的值。您只需删除一个传递给 - 方法的字符( toBe 符号,您的通过测试将如下所示:

it('should return a valid names observables', () => {
scheduler.run(({ cold, hot, expectObservable }) => {
const mockNames: Name[] = [{
title: 'title1',
group: 'group1'
}];
nameStateService.updateFilteredNames(mockNames);
expectObservable(nameStateService.filteredNames$).toBe('b', {b: mockNames});
});
});

关于 Angular 单元测试 : how to use marble testing (rxjs/testing) to test this state management service,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59463528/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com