【问题标题】:How to mock DialogService.open(...).whenClosed(...) with Jasmine?如何用 Jasmine 模拟 DialogService.open(...).whenClosed(...)?
【发布时间】:2018-01-30 04:27:37
【问题描述】:

我们有一些使用 Aurelia 框架和 Dialog 插件的 TypeScript 代码,我们正在尝试使用 Jasmine 进行测试,但无法弄清楚如何正确执行。

这是源函数:

openDialog(action: string) {
    this._dialogService.open({ viewModel: AddAccountWizard })
        .whenClosed(result => {
            if (!result.wasCancelled && result.output) {
                const step = this.steps.find((i) => i.action === action);
                if (step) {
                    step.isCompleted = true;
                }
            }
        });
}

我们可以创建一个 DialogService 间谍,并轻松验证 open 方法 - 但我们无法弄清楚如何让间谍使用模拟结果参数调用 whenClosed 方法,以便我们可以断言该步骤已完成。

这是当前的 Jasmine 代码:

it("opens a dialog when clicking on incomplete bank account", async done => {
    // arrange
    arrangeMemberVerificationStatus();
    await component.create(bootstrap);
    const vm = component.viewModel as GettingStartedCustomElement;
    dialogService.open.and.callFake(() => {
        return { whenClosed: () => Promise.resolve({})};
    });

    // act
    $(".link, .-arrow")[0].click();

    // assert
    expect(dialogService.open).toHaveBeenCalledWith({ viewModel: AddAccountWizard });
    expect(vm.steps[2].isCompleted).toBeTruthy(); // FAILS

    done();
});

【问题讨论】:

    标签: unit-testing aurelia aurelia-dialog


    【解决方案1】:

    我们最近刚刚更新了我们的 DialogService 并遇到了同样的问题,所以我们制作了这个原始的 mock,到目前为止适合我们的目的。它相当有限,不能很好地模拟具有不同结果的多个调用,但应该适用于您的上述情况:

    export class DialogServiceMock {
        shouldCancelDialog = false;
        leaveDialogOpen = false;
        desiredOutput = {};
        open = () => {
            let result = { wasCancelled: this.shouldCancelDialog, output: this.desiredOutput };
            let closedPromise = this.leaveDialogOpen ? new Promise((r) => { }) : Promise.resolve(result);
            let resultPromise = Promise.resolve({ closeResult: closedPromise });
            resultPromise.whenClosed = (callback) => {
                return this.leaveDialogOpen ? new Promise((r) => { }) : Promise.resolve(typeof callback == "function" ? callback(result) : null);
            };
            return resultPromise;
        };
    }
    

    可以配置此模拟来测试各种响应、用户取消对话框时以及对话框仍处于打开状态的情况。

    我们还没有完成 e2e 测试,所以我不知道有什么好的方法可以确保您等到 .click() 调用完成,这样您的 expect() 之间就不会出现竞争条件和 whenClosed() 逻辑,但我认为您应该能够像这样在测试中使用模拟:

    it("opens a dialog when clicking on incomplete bank account", async done => {
        // arrange
        arrangeMemberVerificationStatus();
        await component.create(bootstrap);
        const vm = component.viewModel as GettingStartedCustomElement;
    
        let mockDialogService = new MockDialogService();
        vm.dialogService = mockDialogService; //Or however you're injecting the mock into the constructor; I don't see the code where you're doing that right now.
        spyOn(mockDialogService, 'open').and.callThrough();
    
        // act
        $(".link, .-arrow")[0].click();
        browser.sleep(100)//I'm guessing there's a better way to verify that it's finished with e2e testing, but the point is to make sure it finishes before we assert.
    
        // assert
        expect(mockDialogService.open).toHaveBeenCalledWith({ viewModel: AddAccountWizard });
        expect(vm.steps[2].isCompleted).toBeTruthy(); // FAILS
    
        done();
    });
    

    【讨论】:

    • 谢谢,我们在几个方面改进了这个场景。我们停止使用 whenClosed() API,并将我们所有的 DialogService 调用转换为使用 open 和 close 结果承诺,从而使方法变得完全异步。我们在测试中调用 VM 方法时使用 await,而不是使用按钮单击,如果我们想测试按钮单击是否已连接,我们会在一个单独的测试中执行此操作,该测试监视 VM 方法以断言它被调用。我们还使用 aurelia-framework 中的 TaskQueue 组件来等待所有其他副作用也被处理,然后再处理断言。
    • @Sam,我知道已经有一段时间了,但是,您是如何摆脱whenClosed API 的?我正在尝试async/await dialogService.open 调用,但到目前为止无济于事。
    猜你喜欢
    • 1970-01-01
    • 2021-07-15
    • 2015-05-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-09-04
    • 1970-01-01
    相关资源
    最近更新 更多