使用jasmine间谍返回的组件集成测试可订阅

时间:2018-05-05 13:18:46

标签: angular jasmine

我正在尝试进行组件集成测试(即包括Angular生命周期事件),但我很难用Jasmine模拟服务,这是一个公开Subscribable的服务。 / p>

重现我的场景:

  1. ng new ng-comp-tests
  2. ng generate component my-screen
  3. ng generate service crud
  4. ng generate class item
  5. 将课程更新为:

    export class Item {
      constructor(public name: string) { }
    }
    

    然后使服务看起来像这样:

    export class CrudService {
      private thing = new Subject<Item>();
    
      constructor() {
        setInterval(() => { this.thing.next(new Item('Thing ' + Math.random())); }, 1000);
      }
    
      getThingObservable() {
        return this.thing.asObservable();
      }
    }
    

    将组件更改为此类(并将其作为应用程序组件中的主要内容):

    <p *ngIf="!!thing">{{thing.name}}</p>
    
    export class MyScreenComponent implements OnInit, OnDestroy {
      public thing: Item;
      private thingSubscription: Subscription;
    
      constructor(private service: CrudService) { }
    
      ngOnInit() {
        this.thingSubscription = this.service
          .getThingObservable()
          .subscribe(t => { this.thing = t; });
      }
    
      ngOnDestroy(): void {
        this.thingSubscription.unsubscribe();
      }
    }
    

    最后,将规格更改为:

    describe('MyScreenComponent', () => {
      let component: MyScreenComponent;
      let fixture: ComponentFixture<MyScreenComponent>;
    
      beforeEach(async(() => {
        const serviceSpy = jasmine.createSpyObj('CrudService', {
          getThingObservable: () => new Subject<Item>().asObservable()
        });
    
        TestBed.configureTestingModule({
          declarations: [ MyScreenComponent ],
          providers: [ { provide: CrudService, useValue: serviceSpy } ]
        })
        .compileComponents();
      }));
    
      beforeEach(() => {
        fixture = TestBed.createComponent(MyScreenComponent);
        component = fixture.componentInstance;
        fixture.detectChanges();
      });
    
      it('should create', () => {
        expect(component).toBeTruthy();
      });
    });
    

    这将为测试提供例外:

      

    MyScreenComponent应该创建
      [object ErrorEvent] thrown

    例外情况并非如此有用,Chrome的控制台也不是,因为它显示了这个缩写日志:

    • &#34;无法加载ng:///DynamicTestModule/...ngfactory.js:交叉原始请求...&#34;
    • &#34;无法加载ng:///DynamicTestModule/...ngfactory.js:交叉原始请求...&#34;
    • &#34;未捕获DOMException:无法执行&#39;发送&#39; ....&#34;
    • &#34;清理组件时出错...无法读取属性&#39;取消订阅&#39;未定义...&#34;

    top answer for a related question建议我忘记执行fixture.detectChanges()(这会阻止ngOnInit解雇),但正如您所见,我的代码中确实存在此问题。

    这里肯定有其他问题,但是什么?

1 个答案:

答案 0 :(得分:0)

答案在the jasmine.createSpyObj(...) documentation,说明第二个参数(强调我的):

  

用于创建间谍的方法名称数组,或对象,其键将是方法名称​​,并且值为returnValue

在您的代码中,您为此getThingObservable提供了一个假实现:

getThingObservable: () => new Subject<Item>().asObservable()

有了这个,getThingObservable间谍会在组件调用它时返回一个lambda ,它没有subscribe组件试图做的事情,所以{{1完全失败了。 ngOnInit仅作为辅助效果失败。

您应该直接传递returnValue,如下所示:

ngOnDestroy

然后,当组件调用getThingObservable: new Subject<Item>().asObservable() 时,它将获得一个实际的可观察量,并且事情会没事。

PS。您还可以简化使用getThingObservable

{ of } from 'rxjs/observable/of'

最后,您还可以使用marble tests来更好地控制测试中的可观察对象。