我正在尝试使用 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);
});
}
}
MockService类中重写了getData方法,而直接调用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);
}));
});
第二个 it
应该创建一个 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)是异步的。因此,所有同步事件都会在 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
,所以使用它不是一个选择。这就是同步模拟服务的用武之地。
您可以将服务注入(inject)到您的 it
测试用例中,或者您可以在测试中保留一个变量并对其进行设置,例如
let mockMyService: MockMyService;
beforeEach(() => {
mockMyService = new MockMyService();
TestBed.configureTestingModule({
providers: [
{ provide: MyService, useValue: mockMyService }
]
});
});
注意:您还需要修正您的通过测试,因为您当前的测试由于上述原因无效。
另请参阅:
- 我的帖子 Testing promise in Angular2 ngOnInit有关在测试组件时模拟
ActivatedRoute
同步工作的示例。
关于Angular 2 RC5 测试 Promise 在 ngOnInit 中不起作用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39436230/