【问题标题】:fakeAsync not waiting for async operation to completefakeAsync 不等待异步操作完成
【发布时间】:2017-02-15 04:37:17
【问题描述】:

上下文

我正在测试一个组件,该组件使用基于 observable 的服务来获取一些数据并将其显示用于 i18n 目的。

i18n 服务是根据特定需求定制的。

组件在开发模式下工作(在某些模板中使用,工作正常)但测试失败。

来源

组件

@Component({
    selector     : "i18n",
    template     : '<span [innerHTML]="text"></span><span #wrapper hidden="true"><ng-content></ng-content><span>',
    encapsulation: ViewEncapsulation.None
})
export class I18nComponent implements OnChanges {

    constructor(private i18n:I18n) {
    }

    @ViewChild('wrapper')
    content:ElementRef;

    @Input('key')
    key:string;

    @Input('domain')
    domain:string;

    @Input('variables')
    variables:Variables = [];

    @Input("plural")
    plural:number;

    text:string;

    ngOnChanges():any {
        this.i18n.get(this.key, this.content.nativeElement.innerHTML, this.variables, this.domain).subscribe((res) => {
            this.text = res;
        });
    }
}

I18n.get

public get(key:string,
               defaultValue?:string,
               variables:Variables = {},
               domain?:string):Observable<string>{
    const catalog = {
                         "StackOverflowDomain":
                         {
                             "my-key":"my-value"
                         }
                    };

    return Observable.of(catalog[domain][key]).delay(300);
}

Variables:

export interface Variables {
    [key:string]:any;
}

测试

describe("I18n component", () => {

    beforeEach(() => {
        TestBed.configureTestingModule({
            providers   : [
                I18n,
                {
                    provide : I18N_CONFIG,
                    useValue: {
                        defaultLocale : "fr_FR",
                        variable_start: '~',
                        variable_end  : '~'
                    }
                },
                {
                    provide : I18N_LOADERS,
                    useClass: MockLocaleLoader,
                    multi   : true
                }
            ],
            declarations: [
                I18nComponent
            ]
        });
        fixture = TestBed.createComponent<I18nComponent>(I18nComponent);
        comp = fixture.componentInstance;
    });

    fit("can call I18n.get.", fakeAsync(() => {
        comp.content.nativeElement.innerHTML = "nope";
        comp.key = "test";
        comp.domain = "test domain";
        comp.ngOnChanges();
        tick();
        fixture.detectChanges();
        expect(comp.text).toBe("test value");
    }));

});

问题

测试失败并显示消息:

预期未定义为“测试值”。

错误:1 个周期性计时器仍在队列中。

因为i18n.get在断言被检查之前没有完成它的工作,所以comp.text仍然是undefined

已经尝试过

  • tick 方法调用中添加一个非常高的值,没有任何改变(尝试使用 5000)。
  • 使ngOnChanges 返回一个在this.text = res; 之后立即解析的Promise&lt;void&gt; 并更改fakeAsync 区域以使用done 方法在then 中调用comp.ngOnChanges 进行简单测试。它有效,但ngOnChanges 不应返回Promise,我想要一个干净的解决方案。

【问题讨论】:

  • 您能否提供足够多的get 方法的示例实现来重现该问题。我不确定如何实现它以获取错误
  • 我编辑了问题,为i18n.get添加了一个模型实现
  • 你的真实代码真的使用delay吗?我问的原因是因为我试图从使用fakeAsync 切换到async 看看是否有什么不同,但似乎delay 使用setInterval,不能在async 内部使用。
  • 我的代码没有使用延迟,我只是添加它来模拟异步行为。但由于 Obervable 的异步特性,它应该以相同的方式工作,不会有任何延迟。
  • 就是这样。只要返回Observable.of,我就可以正常工作。我没有问题。这就是为什么我要求一个重现问题的例子:/

标签: angular typescript jasmine karma-jasmine


【解决方案1】:

请注意,asyncfakeasync 不如 jasmine.done 强大和包容

从(在撰写本文时)获取确切的注释: https://angular.io/guide/testing#component-fixture

上面写着:

用 done 编写测试函数,虽然比 async 和 fakeAsync 是一种可行且偶尔需要的技术。为了 例如,在测试代码时不能调用 async 或 fakeAsync 涉及到 intervalTimer,在测试 async Observable 时很常见

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-10-04
    • 2013-06-09
    • 2019-06-16
    • 2018-03-05
    • 2013-08-10
    • 2016-10-24
    • 2019-05-31
    相关资源
    最近更新 更多