跟着華爲DevUI開源組件庫學寫單元測試用例

DevUI是一支兼具設計視角和工程視角的團隊,服務於華爲雲 DevCloud平臺和華爲內部數箇中後臺系統,服務於設計師和前端工程師。
官方網站: devui.design
Ng組件庫: ng-devui(歡迎Star)
官方交流:添加DevUI小助手(devui-official)
DevUIHelper插件:DevUIHelper-LSP(歡迎Star)

引言

做爲一個成熟的web前端工程師,咱們都熟知,編寫單元測試用例,對前端而言,確實收益不大,業務代碼還寫不完呢,寫什麼測試用例吶,大概這是諸多web前端工程師的現狀。css

那麼爲何我還要介紹單元測試用例編寫呢?html

在開發側發現產品質量問題,對於開發來講,是成本最低的一種保證上線產品質量的方法。前端

在開發時編寫測試用例,就是諸多從開發側發現問題的策略之一。git

在對devui組件庫進行單元測試用例編寫的時候,會考慮兩點:github

  1. 首先開源庫已經有了組件的實現,測試驅動開發的場景比較弱;
  2. 若是基於開源組件庫進行開發使用,咱們僅關心咱們本身經常使用的一些功能是否可以保證。

本文將以組件庫中的Button組件爲例,展開對於DevUI開源組件庫單元測試用例編寫的介紹。web

1 編寫單元測試用例前的準備

Step 1: 克隆ng-devui源碼

先將ng-devui組件庫源碼克隆下來,安裝依賴包後保證npm start可以啓動項目:npm

Step 2: 找到啓動單元測試的命令

項目啓動後,前往package.json文件,能夠看到有兩個test命令:json

這個時候可能有些讀者有點懵逼,這兩個命令,我該運行哪一個呢?segmentfault

經過查看angular.json查看應該運行的命令,拉到angular.json文件底部,能夠查看到默認運行的是devui,項目中有三個項目:devuidevui-e2edevui-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: 規劃測試並準備數據

爲了保證文章的不那麼冗餘,這裏只拋磚引玉,僅對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)還有一種情形,你開發任何一個小的組件,好比一個小小的按鈕,均可能會在幾十,上百個業務中使用,你的任何一個失誤,都有如落入湖水的石子,蕩起整個湖面的漣漪。

以上兩種情形,若是開發者沒有測試充分,貿然上線,都會形成重大損失。

穩定:是這兩種開發場景第一需求。

這個時候,但願上述單元測試用例的編寫可以對讀者有所啓發,固然啦,咱們最終的目標依然是以最小的成本解決更多的問題!

加入咱們

咱們是DevUI團隊,歡迎來這裏和咱們一塊兒打造優雅高效的人機設計/研發體系。招聘郵箱:muyang2@huawei.com。

文/DevUI 莫奈的關門弟子

往期文章推薦

《如今開始爲你的Angular應用編寫測試(二)》

《如今開始爲你的Angular應用編寫測試(一)》

《手把手教你搭建一個灰度發佈環境》

相關文章
相關標籤/搜索