【问题标题】:How to mock observable and geter from injected mocked service如何从注入的模拟服务中模拟 observable 和 geter
【发布时间】:2019-04-18 12:10:16
【问题描述】:

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

我有一个服务,我想测试它:

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; }
...

在我的测试中,我想模拟来自FrameStateServicechangedAvailableFrames 以及来自AnimationStateServicestatus

我已经做了什么:

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

此时我完全迷失了,我不知道如何继续。

【问题讨论】:

    标签: angular typescript unit-testing testing jasmine


    【解决方案1】:

    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');
    

    【讨论】:

    • 我需要来自FrameStateServicechangedAvailableFrames Observable,而不是Subject,而不是来自FrameService
    • @Gabriel 现在我明白了……你的测试中有FrameService,这让我很困惑。应该是FrameStateService。更新了代码。
    • 顺便说一下,在间谍对象上创建属性很容易。 spy 对象与任何其他 JavaScript 对象没有什么不同,您只需通过定义它在其上创建一个属性,例如 spyFrameStateService.newProperty = 'foo'
    • @dmcgrandle 您必须在创建测试模块之前执行此操作,因为changedAvailableFramesSubject 必须存在于服务实例化中。有问题的线路:public changedAvailableFrames: Observable&lt;LayerId&gt; = this.changedAvailableFramesSubject.asObservable();
    • @MartinNuc 当然,可以在声明 spyObj 后立即完成。我只是指出,在一般情况下,向间谍对象添加属性很简单。
    猜你喜欢
    • 1970-01-01
    • 2018-04-30
    • 2013-01-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-23
    相关资源
    最近更新 更多