ふるてつのぶろぐ

福岡在住のエンジニアです。

写真提供:福岡市

Angular8 で Web アプリを作ろう - Jasmine - Componentのテスト その2 DOM

今日すること

こんにちは、ふるてつです。

前回、前々回とJasmineユニットテストについて書いていきました。
今回はComponentの DOM 部分のテストになります。

テストをするComponentの概要

今回テストをするのは前回と同様に会社マスターの一覧を検索する画面です。
すごく簡単です、検索条件は「会社名」、「会社名カナ」、「削除フラグ」の3です。
画面は下記になります(レイアウトはざっとしか作っていません今後きれいにします)
f:id:tetsufuru:20190831161753p:plain

DOM のテストを作るにあたり、下記の公式リファレンスを熟読しました。
https://angular.jp/guide/testing

コンポーネントの DOM のテスト」という章がありますので、 もしこれからテストコードを書かれる方はまず最初にこちらを参考にすると良いと思います。

テストをするComponentのHTMLとコード

前回はソースコードのみでしたが、今回はHTMLも掲載します。
それぞれ長いのでたたんでおきます。

テストをするComponentのHTMLはこちら(company-list.component.html)

<div class="mainColor">
  <div class="wrapper mainBody">
    <form [formGroup]="mainForm">
      <!-- Screen Title Area -->
      <div class="titleArea">
        {{ 'companyListScreen.title' | translate }}
      </div>

      <!-- Search Conditions Area -->
      <mat-card>
        <mat-card-content>
          <app-error-messages></app-error-messages>
          <div id="searchConditionsArea">
            <!-- -------------------- 1 -------------------- -->
            <div id="searchCondition1">
              <mat-form-field class="form-field">
                <input id="companyName" matInput type="text" formControlName="companyName"
                  placeholder="{{ 'companyListScreen.companyName' | translate }}">
              </mat-form-field>
            </div>
            <div id="searchCondition2">
              <mat-form-field class="form-field">
                <input id="companyKana" matInput type="text" formControlName="companyKana"
                  placeholder="{{ 'companyListScreen.companyKana' | translate }}">
              </mat-form-field>
            </div>
            <div id="searchCondition3">
              <label class="labelFor">{{ 'companyListScreen.deleted' | translate }}</label>
              <mat-checkbox id="deleted" formControlName="deleted"></mat-checkbox>
            </div>
          </div>
        </mat-card-content>
      </mat-card>

      <!-- Button Area -->
      <div id="searchButtonArea">
        <div id="paginatorArea">
          <mat-paginator [length]="resultsLength" [pageSize]="50" [pageSizeOptions]="[10, 50, 100]"></mat-paginator>
        </div>
        <div id="newBtnArea">
          <button mat-raised-button color="primary" id="newBtn" class="btn" (click)="onNew()"
            type="button">{{ "companyListScreen.newBtn" | translate }}
          </button>
        </div>
        <div id="clearBtnArea">
          <button mat-raised-button color="primary" id="clearBtn" class="btn" (click)="onClear()"
            type="button">{{ "companyListScreen.clearBtn" | translate }}
          </button>
        </div>
        <div id="searchBtnArea">
          <button mat-raised-button color="primary" id="searchBtn" class="btn" type="submit"
            (click)="onSearch()">{{ "companyListScreen.searchBtn" | translate }}
          </button>
        </div>
      </div>

      <!-- evaluationResult Area-->
      <div id="evaluationResult">
        <div class="loading-shade" *ngIf="isLoadingResults">
          <mat-spinner class="loading-spinner" *ngIf="isLoadingResults"></mat-spinner>
        </div>

        <div class="example-container">
          <table mat-table *ngIf="resultsLength>0" [dataSource]="searchCompanyDtos">
            <ng-container matColumnDef="companySeq">
              <th mat-header-cell *matHeaderCellDef style="width: 10%;">
                {{ "companyListScreen.searchResult.companySeq" | translate }}
              </th>
              <td mat-cell *matCellDef="let element"> {{element.companySeq}} </td>
            </ng-container>
            <ng-container matColumnDef="companyName">
              <th mat-header-cell *matHeaderCellDef>
                {{ "companyListScreen.companyName" | translate }}
              </th>
              <td mat-cell *matCellDef="let element"> {{element.companyName}} </td>
            </ng-container>
            <ng-container matColumnDef="companyKana">
              <th mat-header-cell *matHeaderCellDef>
                {{ "companyListScreen.companyKana" | translate }}</th>
              <td mat-cell *matCellDef="let element"> {{element.companyKana}} </td>
            </ng-container>
            <ng-container matColumnDef="companyAddress1">
              <th mat-header-cell *matHeaderCellDef>
                {{ "companyListScreen.searchResult.companyAddress1" | translate }}</th>
              <td mat-cell *matCellDef="let element"> {{element.companyAddress1}} </td>
            </ng-container>
            <ng-container matColumnDef="numOfEmployee">
              <th mat-header-cell *matHeaderCellDef>
                {{ "companyListScreen.searchResult.numOfEmployee" | translate }}</th>
              <td mat-cell *matCellDef="let element"> {{element.numOfEmployee}} </td>
            </ng-container>

            <ng-container matColumnDef="deleted">
              <th mat-header-cell *matHeaderCellDef>
                {{ "companyListScreen.deleted" | translate }}</th>
              <td mat-cell *matCellDef="let element"> {{element.deleted}} </td>
            </ng-container>
            <ng-container matColumnDef="evaluationSetting">
              <th mat-header-cell *matHeaderCellDef>
                {{ "companyListScreen.searchResult.evaluationSetting" | translate }}</th>
              <td mat-cell *matCellDef="let element"> {{element.evaluationSetting}} </td>
            </ng-container>
            <ng-container matColumnDef="createTime">
              <th mat-header-cell *matHeaderCellDef>
                {{ "companyListScreen.searchResult.createTime" | translate }}</th>
              <td mat-cell *matCellDef="let element"> {{element.createTime|date:'medium':timezone:locale}}
              </td>
            </ng-container>
            <ng-container matColumnDef="createUser">
              <th mat-header-cell *matHeaderCellDef>
                {{ "companyListScreen.searchResult.createUser" | translate }}</th>
              <td mat-cell *matCellDef="let element"> {{element.createUser}} </td>
            </ng-container>
            <ng-container matColumnDef="updateTime">
              <th mat-header-cell *matHeaderCellDef>
                {{ "companyListScreen.searchResult.updateTime" | translate }}</th>
              <td mat-cell *matCellDef="let element"> {{element.updateTime|date:'medium':timezone:locale}} </td>
            </ng-container>
            <ng-container matColumnDef="updateUser">
              <th mat-header-cell *matHeaderCellDef>
                {{ "companyListScreen.searchResult.updateUser" | translate }}</th>
              <td mat-cell *matCellDef="let element"> {{element.updateUser}} </td>
            </ng-container>

            <tr mat-header-row *matHeaderRowDef="displayCompanyListColumns; sticky: true"></tr>
            <tr mat-row *matRowDef="let row; columns: displayCompanyListColumns;" (click)="listClicked(row)"></tr>
          </table>
        </div>
      </div>
    </form>
  </div>
</div>

テストをするComponentのソースコードはこちら(company-list.component.ts)

import { merge, of } from 'rxjs';
import { catchError, map, startWith, switchMap } from 'rxjs/operators';
import { AppConst } from 'src/app/app-const';
import { SearchCompanyDto } from 'src/app/entity/company/search-company-dto';
import { CompanyService } from 'src/app/service/company/company.service';

import { HttpParams } from '@angular/common/http';
import { Component, OnInit, ViewChild } from '@angular/core';
import { FormBuilder, FormControl } from '@angular/forms';
import { MatPaginator } from '@angular/material/paginator';
import { Title } from '@angular/platform-browser';
import { Router } from '@angular/router';

@Component({
  selector: 'app-company-list',
  templateUrl: './company-list.component.html',
  styleUrls: ['./company-list.component.css']
})

export class CompanyListComponent implements OnInit {
  // Timezone and Locale
  locale: string;
  timezone: string;

  // Search criteria controls
  companyName = new FormControl('', []);
  companyKana = new FormControl('', []);
  deleted = new FormControl(false);

  // Form builder
  mainForm = this.formBuilder.group({
    companyName: this.companyName,
    companyKana: this.companyKana,
    deleted: this.deleted
  });

  // Search result dto
  searchCompanyDtos: SearchCompanyDto[];

  // Material tables header
  displayCompanyListColumns: string[] = [
    'companySeq',
    'companyName',
    'companyKana',
    'companyAddress1',
    'deleted',
    'createUser',
    'createTime',
    'updateUser',
    'updateTime',
  ];

  // Loading and pagenation
  isLoadingResults = false;
  resultsLength = 0;
  @ViewChild(MatPaginator, { static: true }) public paginator: MatPaginator;

  constructor(
    private formBuilder: FormBuilder,
    private companyService: CompanyService,
    private title: Title,
    private router: Router
  ) { }

  ngOnInit() {
    this.setUpLocale();
    this.setUpBrowserTitle();
  }

  /**
   * Sets the locale from appConst.
   */
  private setUpLocale() {
    this.locale = AppConst.LOCALE;
    this.timezone = AppConst.TIMEZONE;
  }

  /**
   * Sets screen title.
   */
  private setUpBrowserTitle() {
    this.title.setTitle(AppConst.APP_TITLE + AppConst.APP_SUB_TITLE_COMPANY_LIST);
  }

  /**
   * Clicks the new registration button.
   */
  private onNew() {
    this.router.navigate(['/company-detail/new']);
  }

  /**
   * Click the clear button.
   */
  private onClear() {
    this.clearSearchCondition();
    this.clearSearchResultList();
  }

  /**
   * Searches for customer informations.
   */
  private onSearch() {
    merge(this.paginator.page)
      .pipe(
        startWith({}),
        switchMap(() => {
          this.isLoadingResults = true;
          return this.companyService.getCompanyList(this.createHttpParams());
        }),

        map(data => {
          // Flip flag to show that loading has finished.
          this.isLoadingResults = false;
          this.resultsLength = data.resultsLength;
          this.paginator.pageIndex = data.pageIndex;
          return data.searchCompanyDtos;
        }),

        catchError(() => {
          this.isLoadingResults = false;
          return of(null as SearchCompanyDto[]);
        })

      ).subscribe(data => this.searchCompanyDtos = data);
  }

  /**
   * Creates search criterias.
   */
  private createHttpParams(): HttpParams {
    const conditions = {
      companyName: this.companyName.value,
      companyKana: this.companyKana.value,
      deleted: this.deleted.value.toString(),
      pageSize: this.paginator.pageSize.toString(),
      pageIndex: this.paginator.pageIndex.toString()
    };

    const paramsOptions = { fromObject: conditions };
    const params = new HttpParams(paramsOptions);

    return params;
  }

  /**
   * Clears search criteria controls.
   */
  private clearSearchCondition() {
    this.companyName.setValue('');
    this.companyKana.setValue('');
    this.deleted.setValue(false);
  }

  /**
   * Clears search result list.
   */
  private clearSearchResultList() {
    this.searchCompanyDtos = null;
    this.resultsLength = 0;
  }

  /**
   * Clicks search result.
   * @param searchCompanyDto cliked company entity.
   */
  private listClicked(searchCompanyDto: SearchCompanyDto) {
    this.router.navigate(['/company-detail', searchCompanyDto.companySeq]);
  }
}

基本的な DOM の操作方法

レファレンスを見る限り下記のような感じが良いかと思いました。
箇条書きにしてみます。

  1. const debugElement: DebugElement = fixture.debugElement;
    まずfixturedebugElement(Debug用のElement)を取得
    ちなみにこのfixturebeforeEachの中でTestBedのcreateComponent()メソッドから作られます。
  2. const queriedElement = debugElement.query(By.css(' id or class name' ));
    つぎにcssのセレクタを使用して目的の要素を取り出します。
  3. const htmlElement: HTMLElement = queriedElement.nativeElement;

実際に DOM を確認・操作したくなった時には、nativeElementプロパティを通して行います。

DOM のテストケースの追加

では実際にテストケースを追加してみます。

①初期表示のテスト

●検索条件の名前

まずは検索条件の名前をテストしてみます。
検索条件の部分はHTML中では下記のように書いています。
判定できそうなところがplaceholderしかありませんので、<div></div>タグ全体を取り出します。
そして<div></div>のテキストの中に日本語で "企業名" が含まれているかテストします。

<div id="searchCondition1">
  <mat-form-field class="form-field">
    <input id="companyName" matInput type="text" formControlName="companyName"
      placeholder="{{ 'companyListScreen.companyName' | translate }}">
  </mat-form-field>
</div>

下記がテストコードです。
<div></div>の個所はdebugElement.query(By.css('#searchCondition1'))で取得します。
そして最後にhtmlElement.textContentで中にあるテキスト文字を取得しテストします。
実際に動かすとテキスト文字は日本語にはなっておらず | translate より前の 'companyListScreen.companyName' が入ってました。
これは他言語化の為のtranslateサービスを spy にしていて、うまく日本語に変換されていない為と思われます。
やりたかったことと若干違いましたが、これでもテストにはなっているので今回はこれで良しとしました✨

it('should set company name with searchCondition1', () => {
  const debugElement: DebugElement = fixture.debugElement;
  const queriedElement = debugElement.query(By.css('#searchCondition1'));
  const htmlElement: HTMLElement = queriedElement.nativeElement;
  expect(htmlElement.textContent).toContain('companyListScreen.companyName');
});

●検索条件の初期値

次は検索条件それぞれの初期値です。
「企業名」「企業カナ」は初期値は空で、「削除済み」のチェックボックスはチェックOFFです。

it('verify initial value of searchConditions', () => {
  const debugElement: DebugElement = fixture.debugElement;
  let queriedElement = debugElement.query(By.css('#companyName'));
  let htmlInputElement: HTMLInputElement = queriedElement.nativeElement;
  expect(htmlInputElement.textContent).toEqual('');

  queriedElement = debugElement.query(By.css('#companyKana'));
  htmlInputElement = queriedElement.nativeElement;
  expect(htmlInputElement.textContent).toEqual('');

  queriedElement = debugElement.query(By.css('.mat-checkbox-inner-container'));
  htmlInputElement = queriedElement.nativeElement;
  expect(htmlInputElement.checked).toBeUndefined();
});

今回「削除済み」のチェックボックスのcssセレクタだけ、クラス指定(.mat-checkbox-inner-container)にしています。
これはAngular Materialのチェックボックスは、ブラウザで表示されたときに、書いたコードとは別のコードを生成して表示する為でした。
実際にわたしが書いたチェックボックスにはIDを振ったのですが、こちらが使えずしかたなくデバックで調べたクラスを指定しました。

f:id:tetsufuru:20190901174418p:plain Chromeでデバッグすると上記のように「mat-checkbox-inner-container」というクラスの要素が出来ています。

②初期表示以外のテスト

●検索条件の入力

次は検索条件に実際に入力してみます。
htmlInputElementの value に期待値をセットして、dispatchEvent(new Event('input'))で入力イベントを発生させます。
(入力イベントを発生させないと思ったように変数 companyName にバインドしないのでご注意ください)
そしてType Script側の変数 companyName に期待値が等しく入っているかをチェックしました。

it('should entry company name', () => {
  const debugElement: DebugElement = fixture.debugElement;
  const queriedElement = debugElement.query(By.css('#companyName'));
  const htmlInputElement: HTMLInputElement = queriedElement.nativeElement;
  const expectedEntry = 'abcd1234日本語';

  htmlInputElement.value = expectedEntry;
  htmlInputElement.dispatchEvent(new Event('input'));
  expect(component.companyName.value).toEqual(expectedEntry);
});

●HttpParamの値

最後は CompanyService の検索時に引数で渡すHttpParamに、検索条件の DOM の値が正しくセットされるかをテストします。
まず3種類の検索条件にそれぞれの期待値を入力します。
その後、createHttpParamsメソッドを実行してHttpParamが正しいかを検証します。

it('should create http param', () => {
  const debugElement: DebugElement = fixture.debugElement;

  let queriedElement = debugElement.query(By.css('#companyName'));
  const htmlInputElement: HTMLInputElement = queriedElement.nativeElement;
  const expectedEntry = 'abcd1234日本語';
  htmlInputElement.value = expectedEntry;
  htmlInputElement.dispatchEvent(new Event('input'));

  queriedElement = debugElement.query(By.css('#companyKana'));
  const htmlInputElementKana: HTMLInputElement = queriedElement.nativeElement;
  const expectedEntryKana = 'アイウエオカキクケコ';
  htmlInputElementKana.value = expectedEntryKana;
  htmlInputElementKana.dispatchEvent(new Event('input'));

  queriedElement = debugElement.query(By.css('.mat-checkbox-inner-container'));
  const htmlInputElementDeleted: HTMLInputElement = queriedElement.nativeElement;
  htmlInputElementDeleted.click();
  fixture.detectChanges();

  const conditions = {
    companyName: expectedEntry,
    companyKana: expectedEntryKana,
    deleted: 'true',
    pageSize: '50',
    pageIndex: '0',
  };
  const paramsOptions = { fromObject: conditions };
  const expectedHttpParams = new HttpParams(paramsOptions);

  expect(component['createHttpParams']()).toEqual(expectedHttpParams);
});

●テストコード全体

これまでのテストコード全体を下に記します、みなさまのご参考になればと思います。

テストコード(company-list.component.spec.ts)はこちら

import { throwError } from 'rxjs';
import { AppConst } from 'src/app/app-const';
import { HttpLoaderFactory } from 'src/app/app.module';
import { SearchCompanyDto } from 'src/app/entity/company/search-company-dto';
import { SearchCompanyListDto } from 'src/app/entity/company/search-company-list-dto';
import { CompanyService } from 'src/app/service/company/company.service';
import { asyncData } from 'src/app/testing/async-observable-helpers';
import { MaterialModule } from 'src/app/utils/material/material.module';

import { HttpClient, HttpClientModule, HttpParams } from '@angular/common/http';
import { DebugElement, NO_ERRORS_SCHEMA } from '@angular/core';
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { FormBuilder, ReactiveFormsModule } from '@angular/forms';
import { By, Title } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { Router } from '@angular/router';
import { TranslateLoader, TranslateModule, TranslatePipe } from '@ngx-translate/core';

import { CompanyListComponent } from './company-list.component';

describe('CompanyListComponent', () => {
  let component: CompanyListComponent;
  let fixture: ComponentFixture;

  let routerSpy: { navigate: jasmine.Spy };
  let companyServiceSpy: { getCompanyList: jasmine.Spy };
  let translatePipeSpy: { translate: jasmine.Spy };

  beforeEach(async(() => {
    routerSpy = jasmine.createSpyObj('Router', ['navigate']);
    companyServiceSpy = jasmine.createSpyObj('CompanyService', ['getCompanyList']);
    translatePipeSpy = jasmine.createSpyObj('TranslatePipe', ['translate']);

    TestBed.configureTestingModule({
      declarations: [CompanyListComponent],
      schemas: [NO_ERRORS_SCHEMA],
      imports: [ReactiveFormsModule, BrowserAnimationsModule, MaterialModule, HttpClientModule,
        TranslateModule.forRoot({
          loader: {
            provide: TranslateLoader,
            useFactory: HttpLoaderFactory,
            deps: [HttpClient]
          }
        }),
      ],
      providers: [
        FormBuilder,
        Title,
        { provide: Router, useValue: routerSpy },
        { provide: CompanyService, useValue: companyServiceSpy },
        { provide: TranslatePipe, useValue: translatePipeSpy },
      ],
    })
      .compileComponents();
  }));

  beforeEach(() => {
    fixture = TestBed.createComponent(CompanyListComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  it('should create', () => {
    expect(component).toBeTruthy();
  });

  /**
   * Type Script test cases.
   */
  it('locale and timezone should be set when called ngOnInit', () => {
    component.ngOnInit();
    expect(component.locale).toEqual(AppConst.LOCALE);
    expect(component.timezone).toEqual(AppConst.TIMEZONE);
  });

  it('locale and timezone should be set when called setUpLocale', () => {
    component['setUpLocale']();
    expect(component.locale).toEqual(AppConst.LOCALE);
    expect(component.timezone).toEqual(AppConst.TIMEZONE);
  });

  // TBD
  // it('browser title should be set when called setUpBrowserTitle', () => {
  //   component['setUpBrowserTitle']();
  //   expect(component.locale).toEqual(AppConst.LOCALE);
  // });

  it('should navigate when called onNew', () => {
    component['onNew']();
    expect(routerSpy.navigate.calls.count()).toBe(1, 'one call');
  });

  it('should navigate when called onClear', () => {
    fillSearchCriteria(component);
    component['onClear']();
    expect(component.companyName.value).toEqual('');
    expect(component.companyKana.value).toEqual('');
    expect(component.deleted.value).toEqual(false);
  });

  it('should call map operator when called onSearch', async () => {
    const expectedSearchCompanyListDto: SearchCompanyListDto = new SearchCompanyListDto();
    const searchCompanyDto: SearchCompanyDto[] =
      [{
        companySeq: BigInt('1'),
        companyName: 'companyName',
        companyKana: 'companyKana',
        companyAddress1: 'companyAddress1',
        deleted: '',
        createUser: 'createUser',
        createTime: new Date,
        updateUser: 'updateUser',
        updateTime: new Date
      }];
    expectedSearchCompanyListDto.searchCompanyDtos = searchCompanyDto;
    companyServiceSpy.getCompanyList.and.returnValue(asyncData(expectedSearchCompanyListDto));

    await component['onSearch']();
    expect(component.searchCompanyDtos).toEqual(expectedSearchCompanyListDto.searchCompanyDtos);
    expect(component.isLoadingResults).toEqual(false);
    expect(companyServiceSpy.getCompanyList.calls.count()).toBe(1, 'one call');
  });

  it('should catch error when called onSearch', async () => {
    await component['onSearch']();
    companyServiceSpy.getCompanyList.and.returnValue(throwError(''));
    expect(component.isLoadingResults).toEqual(false);
  });

  it('should navigate when called listClicked', () => {
    const searchCompanyDto: SearchCompanyDto = new SearchCompanyDto();
    searchCompanyDto.companySeq = BigInt('1');
    component['listClicked'](searchCompanyDto);
    expect(routerSpy.navigate.calls.count()).toBe(1, 'one call');
  });

  /**
   * DOM test cases.
   */
  it('should set company name with searchCondition1', () => {
    const debugElement: DebugElement = fixture.debugElement;
    const queriedElement = debugElement.query(By.css('#searchCondition1'));
    const htmlElement: HTMLElement = queriedElement.nativeElement;
    expect(htmlElement.textContent).toContain('companyListScreen.companyName');
  });

  it('should set company kana with searchCondition2', () => {
    const debugElement: DebugElement = fixture.debugElement;
    const queriedElement = debugElement.query(By.css('#searchCondition2'));
    const htmlElement: HTMLElement = queriedElement.nativeElement;
    expect(htmlElement.textContent).toContain('companyListScreen.companyKana');
  });

  it('should set deleted with searchCondition3', () => {
    const debugElement: DebugElement = fixture.debugElement;
    const queriedElement = debugElement.query(By.css('#searchCondition3'));
    const htmlElement: HTMLElement = queriedElement.nativeElement;
    expect(htmlElement.textContent).toContain('companyListScreen.deleted');
  });

  it('verify initial value of searchConditions', () => {
    const debugElement: DebugElement = fixture.debugElement;
    let queriedElement = debugElement.query(By.css('#companyName'));
    let htmlInputElement: HTMLInputElement = queriedElement.nativeElement;
    expect(htmlInputElement.textContent).toEqual('');

    queriedElement = debugElement.query(By.css('#companyKana'));
    htmlInputElement = queriedElement.nativeElement;
    expect(htmlInputElement.textContent).toEqual('');

    queriedElement = debugElement.query(By.css('.mat-checkbox-inner-container'));
    htmlInputElement = queriedElement.nativeElement;
    expect(htmlInputElement.checked).toBeUndefined();
  });

  it('should entry company name', () => {
    const debugElement: DebugElement = fixture.debugElement;
    const queriedElement = debugElement.query(By.css('#companyName'));
    const htmlInputElement: HTMLInputElement = queriedElement.nativeElement;
    const expectedEntry = 'abcd1234日本語';

    htmlInputElement.value = expectedEntry;
    htmlInputElement.dispatchEvent(new Event('input'));
    expect(component.companyName.value).toEqual(expectedEntry);
  });

  it('should entry company kana', () => {
    const debugElement: DebugElement = fixture.debugElement;
    const queriedElement = debugElement.query(By.css('#companyKana'));
    const htmlInputElement: HTMLInputElement = queriedElement.nativeElement;
    const expectedEntry = 'アイウエオカキクケコ';

    htmlInputElement.value = expectedEntry;
    htmlInputElement.dispatchEvent(new Event('input'));
    expect(component.companyKana.value).toEqual(expectedEntry);
  });

  /** The material check box is different from normal. */
  it('should entry deleted', () => {
    const debugElement: DebugElement = fixture.debugElement;
    const queriedElement = debugElement.query(By.css('.mat-checkbox-inner-container'));
    const htmlInputElement: HTMLInputElement = queriedElement.nativeElement;

    htmlInputElement.click();
    fixture.detectChanges();
    expect(component.mainForm.value.deleted).toBe(true);

  });

  it('should create http param', () => {
    const debugElement: DebugElement = fixture.debugElement;

    let queriedElement = debugElement.query(By.css('#companyName'));
    const htmlInputElement: HTMLInputElement = queriedElement.nativeElement;
    const expectedEntry = 'abcd1234日本語';
    htmlInputElement.value = expectedEntry;
    htmlInputElement.dispatchEvent(new Event('input'));

    queriedElement = debugElement.query(By.css('#companyKana'));
    const htmlInputElementKana: HTMLInputElement = queriedElement.nativeElement;
    const expectedEntryKana = 'アイウエオカキクケコ';
    htmlInputElementKana.value = expectedEntryKana;
    htmlInputElementKana.dispatchEvent(new Event('input'));

    queriedElement = debugElement.query(By.css('.mat-checkbox-inner-container'));
    const htmlInputElementDeleted: HTMLInputElement = queriedElement.nativeElement;
    htmlInputElementDeleted.click();
    fixture.detectChanges();

    const conditions = {
      companyName: expectedEntry,
      companyKana: expectedEntryKana,
      deleted: 'true',
      pageSize: '50',
      pageIndex: '0',
    };
    const paramsOptions = { fromObject: conditions };
    const expectedHttpParams = new HttpParams(paramsOptions);

    expect(component['createHttpParams']()).toEqual(expectedHttpParams);
  });

});
function fillSearchCriteria(component: CompanyListComponent) {
  component.companyName.setValue('a');
  component.companyKana.setValue('a');
  component.deleted.setValue(true);
}

今日の感想


今日はComponentのテストその2ということで、DOM 側のテストについて書きました。
ひととおり完成したと思いブログを書き始めたのですが、書き終わる今になって、
検索結果の一覧のテストを追加したほうが良い気もしてきました。
Jasmineをはじめて間もないので、もっと良いやり方があればまだまだ工夫していきたいと思います。
少し落ち着いたらそのうちまたJasmineについて記事を書きたいと思います。

では、今日もお疲れ様でした。