我正在尝试将单元测试添加到我的 Angular 2 应用程序中。在我的一个组件中,有一个带有
(click)
处理程序的按钮。当用户单击按钮时,将调用 .ts
类文件中定义的函数。该函数在 console.log 窗口中打印一条消息,表明该按钮已被按下。我当前的测试代码测试打印 console.log
消息:
describe('Component: ComponentToBeTested', () => {
var component: ComponentToBeTested;
beforeEach(() => {
component = new ComponentToBeTested();
spyOn(console, 'log');
});
it('should call onEditButtonClick() and print console.log', () => {
component.onEditButtonClick();
expect(console.log).toHaveBeenCalledWith('Edit button has been clicked!);
});
});
但是,这仅测试控制器类,而不测试 HTML。我不仅仅是想测试调用
onEditButtonClick
时是否发生日志记录;我还想测试当用户单击组件 HTML 文件中定义的编辑按钮时是否调用 onEditButtonClick
。我怎样才能做到这一点?
我的目标是检查当用户单击编辑按钮时是否调用“onEditButtonClick”,而不是仅检查正在打印的 console.log。
您需要首先使用 Angular
TestBed
设置测试。这样您就可以实际抓住按钮并单击它。您要做的就是配置一个模块,就像您配置@NgModule
一样,仅用于测试环境
import { TestBed, async, ComponentFixture } from '@angular/core/testing';
describe('', () => {
let fixture: ComponentFixture<TestComponent>;
let component: TestComponent;
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [ ],
declarations: [ TestComponent ],
providers: [ ]
}).compileComponents().then(() => {
fixture = TestBed.createComponent(TestComponent);
component = fixture.componentInstance;
});
}));
});
然后你需要监视
onEditButtonClick
方法,点击按钮,检查该方法是否被调用了
it('should', async(() => {
spyOn(component, 'onEditButtonClick');
let button = fixture.debugElement.nativeElement.querySelector('button');
button.click();
fixture.whenStable().then(() => {
expect(component.onEditButtonClick).toHaveBeenCalled();
});
}));
这里我们需要运行
async
测试,因为按钮点击包含异步事件处理,需要通过调用fixture.whenStable()
等待事件处理
现在首选使用
fakeAsync/tick
组合,而不是 async/whenStable
组合。如果进行 XHR 调用,则应使用后者,因为 fakeAsync
不支持它。因此,重构上面的代码,它看起来像这样
it('should', fakeAsync(() => {
spyOn(component, 'onEditButtonClick');
let button = fixture.debugElement.nativeElement.querySelector('button');
button.click();
tick();
expect(component.onEditButtonClick).toHaveBeenCalled();
}));
不要忘记导入
fakeAsync
和 tick
。
可以使用
async
提供的fakeAsync
/'@angular/core/testing'
函数来测试事件,因为浏览器中的任何事件都是异步的并推送到事件循环/队列。
下面是一个非常基本的示例,使用
fakeAsync
测试点击事件。
fakeAsync
功能通过在特殊的fakeAsync
测试区域中运行测试主体来实现线性编码风格。这里我正在测试一个由点击事件调用的方法。
it('should', fakeAsync( () => {
fixture.detectChanges();
spyOn(componentInstance, 'method name'); //method attached to the click.
let btn = fixture.debugElement.query(By.css('button'));
btn.triggerEventHandler('click', null);
tick(); // simulates the passage of time until all pending asynchronous activities finish
fixture.detectChanges();
expect(componentInstance.methodName).toHaveBeenCalled();
}));
以下是 Angular 文档 不得不说的内容:
fakeAsync 相对于 async 的主要优点是测试看起来是同步的。没有
会破坏可见的控制流程。承诺返回then(...)
消失了,取而代之的是fixture.whenStable
tick()
有有限制。例如,您无法从
中进行 XHR 调用fakeAsync
我正在使用Angular 6。我按照 Mav55 的回答进行操作,结果成功了。不过我想确定
fixture.detectChanges();
是否真的有必要,所以我删除了它,它仍然有效。然后我删除了tick();
,看看它是否有效,而且确实有效。最后,我从fakeAsync()
包装中取出测试,令人惊讶的是,它起作用了。
所以我最终得到了这个:
it('should call onClick method', () => {
const onClickMock = spyOn(component, 'onClick');
fixture.debugElement.query(By.css('button')).triggerEventHandler('click', null);
expect(onClickMock).toHaveBeenCalled();
});
而且效果很好。
首先要检查按钮调用事件,我们需要监视单击按钮后将调用的方法 所以我们的第一行是spyOn 间谍方法有两个参数 1) 元件名称 2)要监视的方法,即:“onSubmit”记住不要使用“()”,仅需要名称 然后我们需要制作要单击的按钮对象 现在我们必须触发事件处理程序,我们将在其上添加单击事件 那么我们期望我们的代码调用一次提交方法
it('should call onSubmit method',() => {
spyOn(component, 'onSubmit');
let submitButton: DebugElement =
fixture.debugElement.query(By.css('button[type=submit]'));
fixture.detectChanges();
submitButton.triggerEventHandler('click',null);
fixture.detectChanges();
expect(component.onSubmit).toHaveBeenCalledTimes(1);
});
我遇到了类似的问题(详细说明如下),我通过使用
jasmine-core: 2.52
函数解决了它(在 tick
中),其毫秒数与原始 setTimeout
调用中相同(或更大)。
例如,如果我有一个
setTimeout(() => {...}, 2500);
(因此它将在 2500 毫秒后触发),我会调用 tick(2500)
,然后 这将解决问题。
我的组件中的内容,作为单击 Delete 按钮的反应:
delete() {
this.myService.delete(this.id)
.subscribe(
response => {
this.message = 'Successfully deleted! Redirecting...';
setTimeout(() => {
this.router.navigate(['/home']);
}, 2500); // I wait for 2.5 seconds before redirect
});
}
她是我的工作测试:
it('should delete the entity', fakeAsync(() => {
component.id = 1; // preparations..
component.getEntity(); // this one loads up the entity to my component
tick(); // make sure that everything that is async is resolved/completed
expect(myService.getMyThing).toHaveBeenCalledWith(1);
// more expects here..
fixture.detectChanges();
tick();
fixture.detectChanges();
const deleteButton = fixture.debugElement.query(By.css('.btn-danger')).nativeElement;
deleteButton.click(); // I've clicked the button, and now the delete function is called...
tick(2501); // timeout for redirect is 2500 ms :) <-- solution
expect(myService.delete).toHaveBeenCalledWith(1);
// more expects here..
}));
附注关于
fakeAsync
和测试中一般异步的精彩解释可以在这里找到:关于 Angular 2 测试策略的视频 - Julie Ralph,从 8:10 开始,持续 4 分钟:)
如果您没有可供查询的 css 类,您可以对适合您情况的属性使用函数匹配。
it('should call onClick from button element', fakeAsync(() => {
spyOn(component, 'onClick').and.callFake(() => null);
fixture.detectChanges();
const button = fixture.debugElement.query(
(val) => val.attributes['id'] === 'button-1'
);
expect(button).toBeTruthy();
trashCanButton.children[0].triggerEventHandler('click', {});
tick(1000);
expect(component.onClick).toHaveBeenCalledTimes(1);
}));