如何从注入的模拟服务中模拟可观察和获取的对象

时间:2018-11-15 11:37:36

标签: angular typescript unit-testing testing jasmine

我是测试的新手,我已经迷路了。

我有一项要测试的服务:

animation.service.ts

... 
public constructor(private animationStateService: AnimationStateService,
                   frameStateService: FrameStateService) {

     frameStateService.changedAvailableFrames
            .pipe(
                ...
                // Some code in pipe
                ...
            )
            ).subscribe(() => {
                ...
                // Some code in subscribe
                ...
            }
        ) }

public start(): void {

    if (this.animationStateService.status !== AnimationStatus.Stopped) {
        return;
    }

    this.animationStateService.changeStatus(AnimationStatus.Preloading);
    this.preloaderService.preloadAllFrames()
        .subscribe(
            () => {
                this.run();
            }
        ); 
}
...

注入的服务:

frame-state.service.ts

@Injectable()
export class FrameStateService {

private changedAvailableFramesSubject: Subject<LayerId> = new Subject();
public changedAvailableFrames: Observable<LayerId> = this.changedAvailableFramesSubject.asObservable();

public constructor() {}
...

animation-state.service.ts

@Injectable()
export class AnimationStateService {

public get status(): AnimationStatus { return this.state.animation.status; }
...

在测试中,我想模拟changedAvailableFrames中的FrameStateServicestatus中的AnimationStateService

我已经做了:

animation.service.spec.ts

describe("AnimationService", () => {

    let animationService: SpyObj<AnimationService>;
    let animationStateService: SpyObj<AnimationStateService>;
    let frameStateService: SpyObj<FrameStateService>;

    beforeEach(() => {

        const spyFrameStateService = createSpyObj("FrameStateService", [""]);
        const spyAnimationStateService = createSpyObj("AnimationStateService", ["changeStatus", "status"]);

        TestBed.configureTestingModule({
        providers: [
            AnimationService,
            {provide: FrameService, useValue: spyFrameService},
            {provide: AnimationStateService, useValue: spyAnimationStateService},
            ]
        });

        animationService = TestBed.get(AnimationService);
        animationStateService = TestBed.get(AnimationStateService);
        frameStateService = TestBed.get(FrameStateService);

        frameStateService.changedAvailableFrames.and.returnValue(of());

    });

    it("Should call changeStatus", () => {

        // Arrange
        animationStateService.status.and.returnValue(AnimationStatus.Stopped);

        // Act
        animationService.start();

        // Assert
        expect(animationStateService.changeStatus).toHaveBeenCalled();
    });

})

我得到的错误:

TypeError: Cannot read property 'pipe' of undefined

TypeError: Cannot read property 'status' of undefined

这时我完全迷路了,我不知道该怎么办。

1 个答案:

答案 0 :(得分:2)

const spyFrameStateService = createSpyObj("FrameStateService", [""]);

将创建一个没有方法且没有属性的模拟对象(在这种情况下,第二个参数应消失。如果要使用方法hello进行模拟,则使其类似于createSpyObj("FrameStateService", ["hello"]);)。不幸的是,使用createSpyObj无法创建属性。

但是您需要那里的属性changedAvailableFramesSubject,对吗?因为它已由您的测试服务订阅。

因此,不要使用createSpyObj来模拟,而是创建一个简单的对象:

{provide: FrameStateService, useValue: { changedAvailableFramesSubject: new Subject() },

并使其随时随地发出:

const frameStateService = TestBed.get(FrameStateService);
frameStateService.changedAvailableFramesSubject.next('your emitted value');