1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

[Angular]Jasmineテストコード記法

Last updated at Posted at 2023-10-05

はじめに

本記事は expect,it等の説明は記載しません。(記事が溢れてるから)
caseに対するテストコードの書き方を順次備忘録として追記していきます。

前提情報

spec.ts
fixture = TestBed.createComponent(コンポーネントクラス名);

TestBed.createComponentとは、
ComponentFixtureオブジェクトを返すメソッドらしいです。

ComponentFixtureを使用することで、
コンポーネントクラス名のインスタンス自体(要はTS)にアクセスでき、
またコンポーネントのHTML要素(DOM要素)へアクセスできるそうです。

なので、
テスト中にコンポーネントのproperty値を検証したり、
HTML要素の値を検証したりすることが可能になる。

前提

  • 各componentでテストを実行する場合、
    RouterTestingModuleは必ずimportsしてください。
  • 同componentで使用するModuleは全てimportsしてください。
    ⇒errorになります。
    (ngModelを使用しているが、FormsModuleをimportsしていないケース等)
  • toBeやtoContainといった
    expect().〇〇(); の〇〇の部分をMatcherと呼ぶそう。どんな種類があるかを事前に把握する。

CASE

HTMLタグの表示確認
**.html
<p>h10s works!</p>

spec.ts
  it('表示確認',()=>{
    const pTag = fixture.debugElement.query(By.css('p'));
    expect(pTag.nativeElement.textContent).toBe('h10s works!');
  })

image.png

プロパティ値確認
A.ts
public temperature:Number = 16;
spec.ts
  it(('property default value check'), fakeAsync(()=>{
    //対象プロパティ値 取得
    let temperature = component.temperature;
    //プロパティ値が16であること確認
    expect(temperature).toEqual(16);
  }))

image.png

ユーザ操作(onclick)でメソッドが正しく呼ばれるか確認

注意:変数定義箇所は記載していません。

A.html
<span class="mod" [class.bk-color]="isChanged">背景色が変わる</span>

<button class="buttond" (click)="onChange()">
    ボタン
</button>

A.ts
  public onChange(){
    if(this.isChanged){
      return this.isChanged = false;
    }else {
      return this.isChanged = true;
    }
  }
spec.ts
  it(('HTML button click class add'), fakeAsync(()=>{

    //spanタグ 取得
    let spanTag = fixture.debugElement.query(By.css('.mod')).nativeElement;
    //buttonタグ 取得
    let buttonTag = fixture.debugElement.query(By.css('.buttond'));

    buttonTag.triggerEventHandler('click',null);

    fixture.detectChanges();
    tick();

    //spanタグにclassが付与されてること確認
    expect(spanTag.classList).toContain("bk-color");

  }))

image.png

inputタグ ngModel プロパティ値更新されてるか確認
A.html
※isChangedプロパティは定義済みです。

<span class="mod" [class.bk-color]="isChanged">背景色が変わる</span>
<input class="checkinput" type="checkbox" [(ngModel)]="isChanged">
spec.ts
  it(('HTML input click class add'), fakeAsync(()=>{

    //spanタグ 取得
    let spanTag = fixture.debugElement.query(By.css('.mod')).nativeElement;
    //inputタグ 取得
    let inputTag = fixture.debugElement.query(By.css('.checkinput')).nativeElement;

    inputTag.click();

    fixture.detectChanges();

    //spanタグにclassが付与されてること確認
    expect(spanTag.classList).toContain("bk-color");

  }))

image.png

メソッドの返り値確認

spyOnはmethodをモック化します。
つまり、methodの本来のTS実装を置き換えて、テスト用のダミー動作をさせること。

A.ts
  public add(a:number,b:number){
    return a+b;
  }

  public multiply(a:number,b:number){
    return a*b;
  }

  public calculate(a:number,b:number){
    return this.add(a,b)+this.multiply(a,b);
  }

spec.ts

  it('mock化',()=>{
    //add methodの返り値をモック化。
    spyOn(component,'add').and.returnValue(20);
    //multiply methodは本来の実装処理を行う。
    spyOn(component,'multiply').and.callThrough();
    //calculate methodの引数をセット。
    var result = component.calculate(2,3);

  //add methodが呼ばれたこと確認テスト
    expect(component.add).toHaveBeenCalled();
    //multiply methodが呼ばれたこと確認テスト
    expect(component.multiply).toHaveBeenCalled();
    //呼び出した際の引数確認テスト
    expect(component.multiply).toHaveBeenCalledWith(2,3);
    //calculateの返り値が正しいか確認 falseになる。
    //result:26になるため。
    expect(result).toBe(11);
  })

image.png

実装順を変更しました。(黄色下線、赤色下線を入れ替え)
こうすると、mock化前に resultに結果が代入されてるため テスト結果は trueになります。 //11

image.png

メソッド処理でプロパティ値が正しく変更更新されてるか確認
A.ts
  ngOnInit(){
    this.methodTest(false);
  }

  public methodTest(val:boolean){
    if(val){
      this.add = 10;
    }else {
      this.add = 3;
    }
  }
spec.ts

  it(('method add 3 or 10 check'), fakeAsync(()=>{
    const add:Number = component.add;

    spyOn(component,'methodTest');
    component.ngOnInit();

    expect(add).toEqual(3);
  }))

image.png

追加

methodTestメソッドをonChangeメソッドから呼ぶようにします。
onChangeメソッドはHTML側のが定義されています。

A.ts
  public methodTest(val:boolean){
    if(val){
      this.add = 10;
    }else {
      this.add = 3;
    }
  }

  public onChange(){
    if(this.isChanged){
      this.methodTest(false);
      return this.isChanged = false;
    }else {
      this.methodTest(true);
      return this.isChanged = true;
    }
  }


spec.ts

  it(('userIF click methodTest check'), (()=>{
    
    let inputTag = fixture.debugElement.query(By.css('.buttond'));

    inputTag.triggerEventHandler('click',null);
    const spy = spyOn(component,'methodTest');

      expect(spy).toHaveBeenCalled();
      expect(component.add).toEqual(10);

  }))

image.png

画面Xから画面Yへ正しく遷移できてるか確認
A.ts

  ngOnInit(){
    console.log("Acomponent");

    setTimeout(()=>{
      this.router.navigateByUrl('/routerComponent');
    },3000)
  }

A.spec.ts
  let component: AComponent;
  let fixture: ComponentFixture<AComponent>;
  let router: Router;

  beforeEach(() => {
    TestBed.configureTestingModule({
      declarations: [AComponent],
      imports:[
        RouterTestingModule,
        RouterModule
      ]
    });
    fixture = TestBed.createComponent(AComponent);
    component = fixture.componentInstance;
    router = TestBed.inject(Router);
    fixture.detectChanges();

  });

  it('遷移確認', ()=>{
    const spy = spyOn(router, 'navigateByUrl');

    component.ngOnInit();
    tick(3000);
    expect(spy).toHaveBeenCalledWith('/routerComponent');
  })

TestBed.inject(ライブラリ);について
※正しい認識とは少しずれるかもしれませんが、ニュアンスはあってると思います。

TestBed.injectで特定のサービス(Router,HttpClient等)のインスタンスを取得(テスト環境用の)して、
テスト環境でサービスが動作するようにします。

そして、it()の中で、 spyして使用することでモック化(ダミー化)された挙動となる。

子コンポーネントへ渡した値が正しく処理されてるか確認

■Parent側

Parent.html
<app-child
    [headerText]="temp"
></app-child>
Parent.ts
public temp:number = 0;

■Child側

Child.html

<div class="header">
    {{ headerText }}
</div>

Child.ts
@Input() headerText:Number | undefined;
Parent.spec.ts
~~~~~~省略~~~~~~
  let component: ParentComponent;
  let fixture: ComponentFixture<ParentComponent>;
  let componentChild: ChildComponent;

~~~~~~省略~~~~~~
  beforeEach(() => {
    TestBed.configureTestingModule({
      declarations: [
        ParentComponent,
        ChildComponent
      ],
      imports:[
        RouterTestingModule,
        FormsModule,
        HttpClientModule
      ]
    });
    fixture = TestBed.createComponent(ParentComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();

    const childDebugElement = 
    fixture.debugElement.query(By.directive(ChildComponent));
    componentChild = childDebugElement.componentInstance;

  });

  it(('parent child component vind check'), fakeAsync(()=>{

    // component.temp = 55;
    let temp = component.temp;
    fixture.detectChanges();

    //変数tempと変数headerTextの値が一致していること。
    expect(componentChild.headerText).toContain(component.temp);

  }))
~~~~~~省略~~~~~~

image.png

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?