【问题标题】:Jasmine: testing observables with eventsJasmine:使用事件测试 observables
【发布时间】:2016-07-25 03:30:24
【问题描述】:

我正在尝试测试一个 angular2 应用程序。我有一个登录表单,它使用 observable 将数据发送到后端:

doLogin() {
    this.usersService.login(this.model)
        .subscribe((data) => {
            console.log("In observable: " + data.isSuccess);
            if (!data.isSuccess) {
                this.alerts.push({});
            }
        });
}

在测试中,我在服务函数上添加了一个间谍,它返回 observable,以便组件可以对其进行操作:

 usersService.login.and.returnValue(Observable.of(
    <LoginResponse>{
        isSuccess: true
    }));

一切准备就绪后,我在提交按钮上发送一个事件,触发组件中的 doLogin 函数:

submitButton.dispatchEvent(new Event("click"));
fixture.detectChanges();

它工作正常。不幸的是,当我检查是否在测试中调用了 usersService.login 时:

expect(usersService.login).toHaveBeenCalled();

我得到一个错误,因为 observable 没有完成并且登录还没有被调用。

我应该如何确保在 observable 完成后检查我的间谍?

【问题讨论】:

    标签: javascript testing typescript jasmine angular


    【解决方案1】:

    我不知道您如何在组件上配置服务,但是当我覆盖从 TestComponentBuilder 创建的组件的提供者时,它对我有用。

    我们来取样。我有一个返回字符串列表的服务:

    import {Observable} from 'rxjs/Rx';
    
    export class MyService {
      getDogs() {
        return Observable.of([ 's1', 's2', ... ]);
      }
    }
    

    组件使用此服务在单击按钮时异步显示列表:

    @Component({
      selector: 'my-list',
      providers: [MyService],
      template: `
        <ul><li *ngFor="#item of items">{{ item }}</li></ul>
        <div id="test" (click)="test()">Test</div>
      `
    })
    export class MyList implements OnInit {
      items:Array<string>;
      service:MyService;
    
      constructor(private service:MyService) {
      }
    
      test() {
        this.service.getDogs().subscribe(
          (dogs) => {
            this.items = dogs;
          });
      }
    }
    

    我想测试一下,当我点击“测试”按钮时,会调用组件的test方法,间接调用服务的getDogs方法。

    为此,我创建了一个直接实例化服务并使用TestComponentBuilder 加载组件的测试。在这种情况下,我需要在调用createAsync 之前对其调用overrideProviders 方法。这样,您将能够提供您的间谍服务,以便收到呼叫通知。这是一个示例:

    let service:MyService = new MyService();
    
    beforeEach(() => {
        spyOn(service, 'getDogs').and.returnValue(Observable.of(
            ['dog1', 'dog2', 'dog3']));
    
    });
    
    it('should test get dogs', injectAsync([TestComponentBuilder], (tcb: TestComponentBuilder) => {
        return tcb.overrideProviders(MyList, [provide(MyService, { useValue: service })])
                  .createAsync(MyList).then((componentFixture: ComponentFixture) => {
            const element = componentFixture.nativeElement;
            componentFixture.detectChanges();
    
            var clickButton = document.getElementById('test');
            clickButton.dispatchEvent(new Event("click"));
    
            expect(service.getDogs).toHaveBeenCalled();
        });
    }));
    

    编辑

    由于事件是异步触发的,可以考虑使用fakeAsync。后者允许您完全控制何时处理异步处理并将异步事物转换为同步事物。

    您可以将测试处理包装到

    fakeAsync((): void => {
      var clickButton = document.getElementById('test');
      clickButton.dispatchEvent(new Event("click"));
    
      expect(service.getDogs).toHaveBeenCalled();
    });
    

    更多细节,你可以看看这个问题:

    【讨论】:

    • 感谢您的回答。我以相同的方式执行此操作,但在我的情况下,在组件调用服务方法之前会检查 toHaveBeenCalled。
    • 我不确定是否理解 ;-) 组件同步调用服务,服务方法异步执行某些操作并返回一个可观察对象。对吗?
    • 是的,没错,但据我了解,dispatchEvent 是异步的,所以 test() 函数是在异步模式下调用的,并且在调用 getDogs 之前可能会检查期望。
    • 如何执行测试?与业力?
    • 也就是说,如果你想等待 observable 接收数据,你可以考虑使用fakeAsync。后者允许您完全控制何时处理异步处理并将异步事物转换为同步事物;-) 您应该对这个问题感兴趣:stackoverflow.com/questions/35990253/…
    猜你喜欢
    • 2017-11-06
    • 2013-05-29
    • 1970-01-01
    • 2015-02-15
    • 2016-07-12
    • 1970-01-01
    • 1970-01-01
    • 2014-04-29
    • 2023-03-27
    相关资源
    最近更新 更多