如何在大理石测试中模拟服务中的可观察对象
How to mock observable from service in marble testing
我想测试一个包含我的逻辑的 Angular 服务。我将简化我的案例以使其简单明了:
我有 logic$,这是我要测试的,它绑定到 data$,另一个可观察的
@Injectable({
providedIn: 'root',
})
export class MyService {
readonly data = new BehaviorSubject<string>('');
readonly data$ = this.data.asObservable();
readonly logic$ = this.data$.pipe(
map((logic: string) => `Mighty ${logic}`)
)
constructor() {}
}
我希望能够模拟测试中的数据并查看我的逻辑是否按预期运行
describe('MyService', () => {
let myService: MyService;
const testScheduler = new TestScheduler((actual, expected) => {
expect(actual).toEqual(expected);
});
beforeEach(() => {
myService = new MyService();
myService.data$ = of("foo", "bar");
TestBed.configureTestingModule({
providers: [{ provide: MyService, useValue: myService }]
});
});
it('should be created', inject([MyService], (service: MyService) => {
expect(service).toBeTruthy();
}));
it('should be toto lala', inject([MyService], (service: MyService) => {
testScheduler.run(helpers => {
const { expectObservable, cold } = helpers;
const expect$ = "ab";
expectObservable(service.logic$).toBe(expect$, {
a: "Mighty foo",
b: "Mighty bar",
})
});
}));
});
我收到以下错误:
Chrome Headless 97.0.4692.99 (Windows 10) MyService should be toto lala FAILED
Expected $.length = 1 to equal 2.
Expected $[0].notification.value = 'Mighty ' to equal 'Mighty foo'.
Expected $[1] = undefined to equal Object({ frame: 1, notification: Notification({ kind: 'N', value: 'Mighty bar', error: undefined, hasValue: true }) }).
Error: Expected $.length = 1 to equal 2.
Expected $[0].notification.value = 'Mighty ' to equal 'Mighty foo'.
Expected $[1] = undefined to equal Object({ frame: 1, notification: Notification({ kind: 'N', value: 'Mighty bar', error: undefined, hasValue: true }) }).
at <Jasmine>
at TestScheduler.assertDeepEqual (projects/ui-affaire-client/src/lib/components/my-component/my-component.service.spec.ts:11:20)
at node_modules/rxjs/_esm2015/internal/testing/TestScheduler.js:110:1
at <Jasmine>
Chrome Headless 97.0.4692.99 (Windows 10): Executed 2 of 3 (1 FAILED) (0 secs / 0.028 secs)
Chrome Headless 97.0.4692.99 (Windows 10) MyService should be toto lala FAILED
Expected $.length = 1 to equal 2.
Expected $[0].notification.value = 'Mighty ' to equal 'Mighty foo'.
Expected $[1] = undefined to equal Object({ frame: 1, notification: Notification({ kind: 'N', value: 'Mighty bar', error: undefined, hasValue: true }) }).
Error: Expected $.length = 1 to equal 2.
Expected $[0].notification.value = 'Mighty ' to equal 'Mighty foo'.
Expected $[1] = undefined to equal Object({ frame: 1, notification: Notification({ kind: 'N', value: 'Mighty bar', error: undefined, hasValue: true }) }).
at <Jasmine>
at TestScheduler.assertDeepEqual (projects/ui-affaire-client/src/lib/components/my-component/my-component.service.spec.ts:11:20)
at node_modules/rxjs/_esm2015/internal/testing/TestScheduler.js:110:1
Chrome 97.0.4692.99 (Windows 10) MyService should be toto lala FAILED
Expected $.length = 1 to equal 2.
Expected $[0].notification.value = 'Mighty ' to equal 'Mighty foo'.
Expected $[1] = undefined to equal Object({ frame: 1, notification: Notification({ kind: 'N', value: 'Mighty bar', error: undefined, hasValue: true }) }).
Error: Expected $.length = 1 to equal 2.
Expected $[0].notification.value = 'Mighty ' to equal 'Mighty foo'.
Expected $[1] = undefined to equal Object({ frame: 1, notification: Notification({ kind: 'N', value: 'Mighty bar', error: undefined, hasValue: true }) }).
at <Jasmine>
at TestScheduler.assertDeepEqual (projects/ui-affaire-client/src/lib/components/my-component/my-component.service.spec.ts:11:20)
at node_modules/rxjs/_esm2015/internal/testing/TestScheduler.js:110:1
at <Jasmine>
Chrome Headless 97.0.4692.99 (Windows 10): Executed 2 of 3 (1 FAILED) (skipped 1) (0.097 secs / 0.028 secs)
Chrome 97.0.4692.99 (Windows 10): Executed 1 of 3 (1 FAILED) (0 secs / 0.019 secs)
Chrome 97.0.4692.99 (Windows 10) MyService should be toto lala FAILED
Expected $.length = 1 to equal 2.
Expected $[0].notification.value = 'Mighty ' to equal 'Mighty foo'.
Expected $[1] = undefined to equal Object({ frame: 1, notification: Notification({ kind: 'N', value: 'Mighty bar', error: undefined, hasValue: true }) }).
Error: Expected $.length = 1 to equal 2.
Expected $[0].notification.value = 'Mighty ' to equal 'Mighty foo'.
Expected $[1] = undefined to equal Object({ frame: 1, notification: Notification({ kind: 'N', value: 'Mighty bar', error: undefined, hasValue: true }) }).
at <Jasmine>
at TestScheduler.assertDeepEqual (projects/ui-affaire-client/src/lib/components/my-component/my-component.service.spec.ts:11:20)
Chrome Headless 97.0.4692.99 (Windows 10): Executed 2 of 3 (1 FAILED) (skipped 1) (0.097 secs / 0.028 secs)
Chrome 97.0.4692.99 (Windows 10): Executed 2 of 3 (1 FAILED) (skipped 1) (0.099 secs / 0.026 secs)
TOTAL: 2 FAILED, 2 SUCCESS
TOTAL: 2 FAILED, 2 SUCCESS
据我所知,显然我的模拟不起作用。 logic$ 的长度为 1,数据仅发出 ''(由 behaviorSubject 发出)
我的猜测是我的模拟是在创建服务之后完成的,因此,“旧”可观察对象仍在使用而不是被替换。
有没有办法正确替换可观察对象以测试服务中的逻辑?
当您创建 MyService
的新实例时,属性 logic$
会使用 data$
的 初始值 进行分配].
即使您之后更改 data$
的值,logic$
属性 也不会是 re-evaluated,因此它会保持相同的初始值(使用BehaviorSubject 作为来源)。
要使用弹珠测试 logic$
,您可以这样做:
it('should be toto lala', inject([MyService], (service: MyService) => {
testScheduler.run((helpers) => {
const { expectObservable, cold } = helpers;
const sourceMarbles = '-1-2';
const expectedMarbles = 'ia-b';
// create cold obs as source
const source = cold(sourceMarbles, { 1: 'foo', 2: 'bar' });
// subscribe BehaviourSubject to source to relay emitted values
source.subscribe(service.data);
// test the output of logic$
expectObservable(service.logic$).toBe(expectedMarbles, {
i: 'Mighty ', // <- this accounts for the initial value of the Behavior
a: 'Mighty foo',
b: 'Mighty bar',
});
});
}));
干杯
我想测试一个包含我的逻辑的 Angular 服务。我将简化我的案例以使其简单明了:
我有 logic$,这是我要测试的,它绑定到 data$,另一个可观察的
@Injectable({
providedIn: 'root',
})
export class MyService {
readonly data = new BehaviorSubject<string>('');
readonly data$ = this.data.asObservable();
readonly logic$ = this.data$.pipe(
map((logic: string) => `Mighty ${logic}`)
)
constructor() {}
}
我希望能够模拟测试中的数据并查看我的逻辑是否按预期运行
describe('MyService', () => {
let myService: MyService;
const testScheduler = new TestScheduler((actual, expected) => {
expect(actual).toEqual(expected);
});
beforeEach(() => {
myService = new MyService();
myService.data$ = of("foo", "bar");
TestBed.configureTestingModule({
providers: [{ provide: MyService, useValue: myService }]
});
});
it('should be created', inject([MyService], (service: MyService) => {
expect(service).toBeTruthy();
}));
it('should be toto lala', inject([MyService], (service: MyService) => {
testScheduler.run(helpers => {
const { expectObservable, cold } = helpers;
const expect$ = "ab";
expectObservable(service.logic$).toBe(expect$, {
a: "Mighty foo",
b: "Mighty bar",
})
});
}));
});
我收到以下错误:
Chrome Headless 97.0.4692.99 (Windows 10) MyService should be toto lala FAILED
Expected $.length = 1 to equal 2.
Expected $[0].notification.value = 'Mighty ' to equal 'Mighty foo'.
Expected $[1] = undefined to equal Object({ frame: 1, notification: Notification({ kind: 'N', value: 'Mighty bar', error: undefined, hasValue: true }) }).
Error: Expected $.length = 1 to equal 2.
Expected $[0].notification.value = 'Mighty ' to equal 'Mighty foo'.
Expected $[1] = undefined to equal Object({ frame: 1, notification: Notification({ kind: 'N', value: 'Mighty bar', error: undefined, hasValue: true }) }).
at <Jasmine>
at TestScheduler.assertDeepEqual (projects/ui-affaire-client/src/lib/components/my-component/my-component.service.spec.ts:11:20)
at node_modules/rxjs/_esm2015/internal/testing/TestScheduler.js:110:1
at <Jasmine>
Chrome Headless 97.0.4692.99 (Windows 10): Executed 2 of 3 (1 FAILED) (0 secs / 0.028 secs)
Chrome Headless 97.0.4692.99 (Windows 10) MyService should be toto lala FAILED
Expected $.length = 1 to equal 2.
Expected $[0].notification.value = 'Mighty ' to equal 'Mighty foo'.
Expected $[1] = undefined to equal Object({ frame: 1, notification: Notification({ kind: 'N', value: 'Mighty bar', error: undefined, hasValue: true }) }).
Error: Expected $.length = 1 to equal 2.
Expected $[0].notification.value = 'Mighty ' to equal 'Mighty foo'.
Expected $[1] = undefined to equal Object({ frame: 1, notification: Notification({ kind: 'N', value: 'Mighty bar', error: undefined, hasValue: true }) }).
at <Jasmine>
at TestScheduler.assertDeepEqual (projects/ui-affaire-client/src/lib/components/my-component/my-component.service.spec.ts:11:20)
at node_modules/rxjs/_esm2015/internal/testing/TestScheduler.js:110:1
Chrome 97.0.4692.99 (Windows 10) MyService should be toto lala FAILED
Expected $.length = 1 to equal 2.
Expected $[0].notification.value = 'Mighty ' to equal 'Mighty foo'.
Expected $[1] = undefined to equal Object({ frame: 1, notification: Notification({ kind: 'N', value: 'Mighty bar', error: undefined, hasValue: true }) }).
Error: Expected $.length = 1 to equal 2.
Expected $[0].notification.value = 'Mighty ' to equal 'Mighty foo'.
Expected $[1] = undefined to equal Object({ frame: 1, notification: Notification({ kind: 'N', value: 'Mighty bar', error: undefined, hasValue: true }) }).
at <Jasmine>
at TestScheduler.assertDeepEqual (projects/ui-affaire-client/src/lib/components/my-component/my-component.service.spec.ts:11:20)
at node_modules/rxjs/_esm2015/internal/testing/TestScheduler.js:110:1
at <Jasmine>
Chrome Headless 97.0.4692.99 (Windows 10): Executed 2 of 3 (1 FAILED) (skipped 1) (0.097 secs / 0.028 secs)
Chrome 97.0.4692.99 (Windows 10): Executed 1 of 3 (1 FAILED) (0 secs / 0.019 secs)
Chrome 97.0.4692.99 (Windows 10) MyService should be toto lala FAILED
Expected $.length = 1 to equal 2.
Expected $[0].notification.value = 'Mighty ' to equal 'Mighty foo'.
Expected $[1] = undefined to equal Object({ frame: 1, notification: Notification({ kind: 'N', value: 'Mighty bar', error: undefined, hasValue: true }) }).
Error: Expected $.length = 1 to equal 2.
Expected $[0].notification.value = 'Mighty ' to equal 'Mighty foo'.
Expected $[1] = undefined to equal Object({ frame: 1, notification: Notification({ kind: 'N', value: 'Mighty bar', error: undefined, hasValue: true }) }).
at <Jasmine>
at TestScheduler.assertDeepEqual (projects/ui-affaire-client/src/lib/components/my-component/my-component.service.spec.ts:11:20)
Chrome Headless 97.0.4692.99 (Windows 10): Executed 2 of 3 (1 FAILED) (skipped 1) (0.097 secs / 0.028 secs)
Chrome 97.0.4692.99 (Windows 10): Executed 2 of 3 (1 FAILED) (skipped 1) (0.099 secs / 0.026 secs)
TOTAL: 2 FAILED, 2 SUCCESS
TOTAL: 2 FAILED, 2 SUCCESS
据我所知,显然我的模拟不起作用。 logic$ 的长度为 1,数据仅发出 ''(由 behaviorSubject 发出) 我的猜测是我的模拟是在创建服务之后完成的,因此,“旧”可观察对象仍在使用而不是被替换。
有没有办法正确替换可观察对象以测试服务中的逻辑?
当您创建 MyService
的新实例时,属性 logic$
会使用 data$
的 初始值 进行分配].
即使您之后更改 data$
的值,logic$
属性 也不会是 re-evaluated,因此它会保持相同的初始值(使用BehaviorSubject 作为来源)。
要使用弹珠测试 logic$
,您可以这样做:
it('should be toto lala', inject([MyService], (service: MyService) => {
testScheduler.run((helpers) => {
const { expectObservable, cold } = helpers;
const sourceMarbles = '-1-2';
const expectedMarbles = 'ia-b';
// create cold obs as source
const source = cold(sourceMarbles, { 1: 'foo', 2: 'bar' });
// subscribe BehaviourSubject to source to relay emitted values
source.subscribe(service.data);
// test the output of logic$
expectObservable(service.logic$).toBe(expectedMarbles, {
i: 'Mighty ', // <- this accounts for the initial value of the Behavior
a: 'Mighty foo',
b: 'Mighty bar',
});
});
}));
干杯