我试图用Jasmine测试名为MyDirective的结构指令.使用的Angular版本是RC5.
// Part of the MyDirective class @Directive({selector: '[myDirective]'}) export class MyDirective { constructor(protected templateRef: TemplateRef<any>,protected viewContainer: ViewContainerRef,protected myService: MyService) { } ngOnInit() { this.myService.getData() .then((data) => { if (!MyService.isValid(data)) { this.viewContainer.createEmbeddedView(this.templateRef); } else { this.viewContainer.clear(); } }) .catch((error) => { console.log(error); this.viewContainer.createEmbeddedView(this.templateRef); }); } }
getData方法在MockService类中被覆盖,而isValid方法(MyService的静态方法)被直接调用,它检查数据的有效性.
// Part of the Jasmine unit test class for the MyDirective class @Component({ selector: 'test-cmp',template: '',directives: [MyDirective] }) class TestComponent {} class MockService { mockResponse: MyResponse = {valid date goes here}; mockInvalidResponse: MyResponse = {}; getData() { if (booleanCondition) { return Promise.resolve(this.mockResponse); } else { return Promise.resolve(this.mockInvalidResponse); } } } describe('MyDirective',() => { beforeEach(() => { TestBed.configureTestingModule({ declarations: [TestComponent],providers: [ {provide: MyService,useClass: MockService},TemplateRef,ViewContainerRef ] }); }); it('should remove the target DOM element when the condition is true',async(() => { booleanCondition = true; const template = '<div><div *myDirective><span>Hi</span></div></div>'; TestBed.overrideComponent(TestComponent,{set: {template: template}}); let fixture = TestBed.createComponent(TestComponent); fixture.detectChanges(); expect(getDOM().querySelectorAll(fixture.debugElement.nativeElement,'span').length).toEqual(0); })); it('should contain the target DOM element when the condition is false',async(() => { booleanCondition = false; const template = '<div><div *myDirective><span>Hi</span></div></div>'; TestBed.overrideComponent(TestComponent,{set: {template: template}}); let fixture = TestBed.createComponent(TestComponent); fixture.detectChanges(); // The 'expect' bellow fails because the value is 0 for some reason expect(getDOM().querySelectorAll(fixture.debugElement.nativeElement,'span').length).toEqual(1); })); });
第二个应该是创建一个span元素在DOM中的情况,但它没有.我检查了它是否会转到if语句中的第一个条件,如下所示:
if (!MyService.isValid(data)) { console.log('the first if condition is read.'); this.viewContainer.createEmbeddedView(this.templateRef); } else { this.viewContainer.clear(); } }
并记录它.所以,它应该将元素保留在DOM中,但我找不到测试它的方法.
因为Promise(从getData返回的)是异步的.因此,在Promise活动之前处理所有同步活动.即使调用了ngOnInit,也会异步解析Promise.
我经常使用这种类型的选项.
一种选择是使用fakeAsync
而不是异步.这允许您调用tick以允许异步操作同步完成
import { fakeAsync,tick } from '@angular/core/testing'; it('... when the condition is false',fakeAsync(() => { const template = '<div><div *myDirective><span>Hi</span></div></div>'; TestBed.overrideComponent(TestComponent,{ set: { template: template } }); let fixture = TestBed.createComponent(TestComponent); fixture.detectChanges(); // tick can also be called with a millisecond delay argument `tick(1000)` tick(); expect(getDOM().querySelectorAll(fixture.debugElement.nativeElement,'span').length) .toEqual(1); }));
另一种选择是使模拟服务同步.您可以通过调用getData()返回服务本身,然后向服务添加then和catch方法来轻松完成此操作.例如
class MockMyService { data; error; getData() { return this; } then(callback) { if (!this.error) { callback('mockData'); } return this; } catch(callback) { if (this.error) { callback(this.error); } } setData(data) { this.data = data; } setError(error) { this.error = error; } }
这种方法的一个优点是它可以在测试执行期间为您提供更多的服务控制.在测试使用templateUrl的组件时,这也非常有用. XHR calls can’t be made in a fakeAsync
,所以使用它不是一种选择.这是同步模拟服务的使用地点.
您可以将服务注入到它的测试用例中,也可以在测试中保留一个变量并将其设置为类似的
let mockMyService: MockMyService; beforeEach(() => { mockMyService = new MockMyService(); TestBed.configureTestingModule({ providers: [ { provide: MyService,useValue: mockMyService } ] }); });
注意:您还需要修复通过测试,因为您当前的测试由于上述原因而无效.
也可以看看:
>我在Testing promise in Angular2 ngOnInit中的帖子中提供了一个模拟ActivatedRoute以在测试组件时同步工作的示例.