51Testing软件测试论坛

 找回密码
 (注-册)加入51Testing

QQ登录

只需一步,快速开始

微信登录,快人一步

查看: 4667|回复: 0
打印 上一主题 下一主题

[转贴] 跟着华为DevUI开源组件库学写单元测试用例

[复制链接]
  • TA的每日心情
    无聊
    昨天 09:14
  • 签到天数: 938 天

    连续签到: 5 天

    [LV.10]测试总司令

    跳转到指定楼层
    1#
    发表于 2021-6-3 09:33:13 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
    1测试积点
    引言
      作为一个成熟的web前端工程师,我们都熟知,编写单元测试用例,对前端而言,确实收益不大,业务代码还写不完呢,写什么测试用例呐,大概这是诸多web前端工程师的现状。
      那么为什么我还要介绍单元测试用例编写呢?
      在开发侧发现产品质量问题,对于开发来说,是成本最低的一种保证上线产品质量的方法。
      在开发时编写测试用例,就是诸多从开发侧发现问题的策略之一。
      在对devui组件库进行单元测试用例编写的时候,会考虑两点:
      1.首先开源库已经有了组件的实现,测试驱动开发的场景比较弱;
      2.如果基于开源组件库进行开发使用,我们仅关心我们自己常用的一些功能是否能够保证。
      本文将以组件库中的Button组件为例,展开对于DevUI开源组件库单元测试用例编写的介绍。
      1、编写单元测试用例前的准备
      Step 1: 克隆ng-devui源码
      先将ng-devui组件库源码克隆下来,安装依赖包后保证npm start能够启动项目:

      Step 2: 找到启动单元测试的命令
      项目启动后,前往package.json文件,可以看到有两个test命令:

      这个时候可能有些读者有点懵逼,这两个命令,我该运行哪个呢?
      通过查看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

      Step 3: 运行单元测试
      运行完npm run test:lib命令,可以看到DevUI组件库包含666个单元测试用例,其中5个跳过,661个成功,0个失败。

      2、开始编写Button组件的单元测试用例
      Step 1: 找到Button组件的单元测试文件
      打开button组件的源码目录,点击进入button的测试文件button.spec.ts,将遇到的第一个describe改写成fdescribe,重新运行npm run test:lib,此时会发现只有15个运行成功的用例,在button.spec.ts文件中搜索it,会发现有15个it字段。
      实际上,一个it即为一个单元测试用例,fdescribe表示,运行的时候只运行整个fdescribe下包含的it单元测试用例。

      Step 2: 规划测试并准备数据
      为了保证[url=]文章[/url]的不那么冗余,这里只抛砖引玉,仅对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();
      }));


      Step 3: 测试Button组件实例创建
      为了保证button组件没有语法类的致命错误,首先应该能够保证组件实例能够创建成功,这也是我们第一步需要进行的测试。
      it('Button demo has created successfully', () => {
        expect(testComponent).toBeTruthy();
      });


      Step 4: 测试Button样式
      保证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();
      });


      Step 5: 测试Button点击功能
      保证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();
      });


      Step 6: 测试禁用功能
      对于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();
      });


      3、对质量的敬畏之心
      (1)作为前端开发,相信大多数开发都在业务开发中摸爬滚打,如果你遇到的业务对质量要求极高,任何一个重大事故都可能影响到诸多客户,拉低客户的效率,给客户造成损失。这种情况,你该如何保证你的代码质量?
      (2)还有一种情形,你开发任何一个小的组件,比如一个小小的按钮,都可能会在几十,上百个业务中使用,你的任何一个失误,都有如落入湖水的石子,荡起整个湖面的涟漪。
      以上两种情形,如果开发者没有测试充分,贸然上线,都会造成重大损失。
      稳定:是这两种开发场景第一需求。
      这个时候,希望上述单元测试用例的编写能够对读者有所启发,当然啦,我们最终的目标依然是以最小的成本解决更多的问题!


    分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
    收藏收藏
    回复

    使用道具 举报

    本版积分规则

    关闭

    站长推荐上一条 /1 下一条

    小黑屋|手机版|Archiver|51Testing软件测试网 ( 沪ICP备05003035号 关于我们

    GMT+8, 2024-4-27 19:40 , Processed in 0.068602 second(s), 21 queries .

    Powered by Discuz! X3.2

    © 2001-2024 Comsenz Inc.

    快速回复 返回顶部 返回列表