DevUI是一支兼具设计视角和工程视角的团队,服务于华为云DevCloud平台和华为内部数个中后台系统,服务于设计师和前端工程师。
官方网站:devui.design
Ng组件库:ng-devui(欢迎Star)
官方交流:添加DevUI小助手(devui-official)
DevUIHelper插件:DevUIHelper-LSP(欢迎Star)css
做为一个成熟的web前端工程师,咱们都熟知,编写单元测试用例,对前端而言,确实收益不大,业务代码还写不完呢,写什么测试用例呐,大概这是诸多web前端工程师的现状。html
那么为何我还要介绍单元测试用例编写呢?前端
在开发侧发现产品质量问题,对于开发来讲,是成本最低的一种保证上线产品质量的方法。git
在开发时编写测试用例,就是诸多从开发侧发现问题的策略之一。github
在对devui组件库进行单元测试用例编写的时候,会考虑两点:web
本文将以组件库中的Button组件为例,展开对于DevUI开源组件库单元测试用例编写的介绍。npm
先将ng-devui组件库源码克隆下来,安装依赖包后保证npm start可以启动项目:json
项目启动后,前往package.json文件,能够看到有两个test命令:markdown
这个时候可能有些读者有点懵逼,这两个命令,我该运行哪一个呢?前端工程师
经过查看angular.json查看应该运行的命令,拉到angular.json文件底部,能够查看到默认运行的是devui,项目中有三个项目:devui
、devui-e2e
、devui-lib
。
devui的根路径是src
,devui-lib的根路径是devui
,而DevUI组件库的全部组件都在devui路径之下:
根据上述判断可知,DevUI组件库的项目应该是devui-lib
。
所以咱们应该运行的package.json
中的test:lib
命令:
npm run test:lib
复制代码
运行完npm run test:lib
命令,能够看到DevUI组件库包含666个单元测试用例,其中5个跳过,661个成功,0个失败。
打开button组件的源码目录,点击进入button的测试文件button.spec.ts,将遇到的第一个describe改写成fdescribe,从新运行npm run test:lib
,此时会发现只有15个运行成功的用例,在button.spec.ts文件中搜索it,会发现有15个it字段。
实际上,一个it即为一个单元测试用例,fdescribe表示,运行的时候只运行整个fdescribe下包含的it单元测试用例。
为了保证文章的不那么冗余,这里只抛砖引玉,仅对button的部分功能进行单元测试,了解了单元测试用例运行的基本方式以后,就能够开始着手单元测试用例的编写。
编写前将现有的button.spec.ts文件内容删除,以便咱们从0开始编写单元测试用例。
编写测试用例时,要先将button组件引入,其中,beforeEach表示每一个it单元测试用例执行前都会执行的操做。
@Component({
template: `
<d-button></d-button>
`
})
class TestButtonAutoFocusComponent {}
fdescribe('Button', () => {
let fixture: ComponentFixture<any>;
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [ButtonModule],
declarations: [TestButtonComponent, TestButtonAutoFocusComponent]
}).compileComponents();
}));
...
});
复制代码
这里将会对button的基本样式,common样式、按钮点击功能及禁用状态进行测试,所以,咱们会在TestButtonComponent测试组件中的button上加上样式、是否禁用、点击函数三个功能。
@Component({
template: `
<d-button [bsStyle]="style" (btnClick)="isClick()" [disabled]="disabled"></d-button>
`
})
class TestButtonComponent {
style = 'primary';
disabled = false;
hasClick = false;
isClick() {
this.hasClick = true;
}
}
复制代码
为了测试button相关的功能,咱们须要建立一个TestButtonComponent的实例,以后还要可以获取到这个实例中使用到的button元素,测试时每每会用到四个变量。
let fixture: ComponentFixture<any>;
let testComponent: TestButtonComponent;
let buttonDebug: DebugElement;
let buttonNative: HTMLElement;
beforeEach((() => {
fixture = TestBed.createComponent(TestButtonComponent);
testComponent = fixture.debugElement.componentInstance;
buttonDebug = fixture.debugElement.query(By.css('d-button'));
buttonNative = buttonDebug.nativeElement;
fixture.detectChanges();
}));
复制代码
为了保证button组件没有语法类的致命错误,首先应该可以保证组件实例可以建立成功,这也是咱们第一步须要进行的测试。
it('Button demo has created successfully', () => {
expect(testComponent).toBeTruthy();
});
复制代码
保证button组件的样式,则须要知道组件可以正确的应用到样式类。
a) 经过查看button.component.html文件可知,正确的应用devui-btn,devui-btn-primary两个类便可表示button可以正确应用到样式类。这样,只要样式文件中的样式类正确,样式就正确。
b) 正确应用devui-btn,devui-btn-common两个类即表示common类型的button样式正确。
it('Button should apply css classes', () => {
let buttonHtml = buttonDebug.query(By.css('button'));
expect(buttonInsideNativeElement.classList.contains('devui-btn')).toBeTruthy();
expect(buttonInsideNativeElement.classList.contains('devui-btn-primary')).toBeTruthy();
testComponent.style = 'common';
fixture.detectChanges();
buttonHtml = buttonDebug.query(By.css('button'));
expect(buttonInsideNativeElement.classList.contains('devui-btn')).toBeTruthy();
expect(buttonInsideNativeElement.classList.contains('devui-btn-common')).toBeTruthy();
});
复制代码
保证button功能正常,则须要知道点击button后,可否触发绑定的isClick事件,触发click事件成功,则hasClick的值将由false变动为true。
a)首先,咱们应该判断一下hasClick的初始赋值是否正确;
b)接着,模拟点击button按钮所绑定的点击事件。
it('Button click', () => {
expect(testComponent.hasClick).toBeFalsy();
let buttonHtml = buttonDebug.query(By.css('button'));
buttonHtml.nativeElement.click();
fixture.detectChanges();
expect(testComponent.hasClick).toBeTruthy();
});
复制代码
对于disabled状态下,点击无效是其功能上的一大特色,同时disabled状态的button具备disabled属性,因此咱们的单元测试也是分为两个部分:
a)点击失效
b) button元素具备disabled属性
it('Button disabled should have disabled attribute', () => {
testComponent.disabled = true;
fixture.detectChanges();
// 点击失效
expect(testComponent.hasClick).toBeFalsy();
let buttonHtml = buttonDebug.query(By.css('button'));
buttonHtml.nativeElement.click();
fixture.detectChanges();
expect(testComponent.hasClick).toBeFalsy();
// 具备disabled样式
expect(buttonHtml.nativeElement.hasAttribute('disabled')).toBeTruthy();
});
复制代码
(1)做为前端开发,相信大多数开发都在业务开发中摸爬滚打,若是你遇到的业务对质量要求极高,任何一个重大事故均可能影响到诸多客户,拉低客户的效率,给客户形成损失。这种状况,你该如何保证你的代码质量?
(2)还有一种情形,你开发任何一个小的组件,好比一个小小的按钮,均可能会在几十,上百个业务中使用,你的任何一个失误,都有如落入湖水的石子,荡起整个湖面的涟漪。
以上两种情形,若是开发者没有测试充分,贸然上线,都会形成重大损失。
稳定:是这两种开发场景第一需求。
这个时候,但愿上述单元测试用例的编写可以对读者有所启发,固然啦,咱们最终的目标依然是以最小的成本解决更多的问题!
咱们是DevUI团队,欢迎来这里和咱们一块儿打造优雅高效的人机设计/研发体系。招聘邮箱:muyang2@huawei.com。
文/DevUI 莫奈的关门弟子
往期文章推荐