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

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

:::note info
前提情報

```spec.ts
fixture = TestBed.createComponent(コンポーネントクラス名);
```
TestBed.createComponentとは、
ComponentFixtureオブジェクトを返すメソッドらしいです。

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

なので、
テスト中にコンポーネントのproperty値を検証したり、
HTML要素の値を検証したりすることが可能になる。
:::
<!--はじめに end-->
<!--前提 start-->
## 前提
* 各componentでテストを実行する場合、
RouterTestingModuleは必ずimportsしてください。
* 同componentで使用するModuleは全てimportsしてください。
⇒errorになります。
(ngModelを使用しているが、FormsModuleをimportsしていないケース等)
* toBeやtoContainといった
expect().〇〇(); の〇〇の部分をMatcherと呼ぶそう。どんな種類があるかを事前に把握する。

https://jestjs.io/ja/docs/using-matchers

https://qiita.com/mask610/items/1d0105e6c389fca9e011
<!--前提 end-->
<!--CASE start-->
## CASE

<details><summary>HTMLタグの表示確認</summary>

```**.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](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/677104/a0eee319-3ba1-98f1-2d82-8b7279a1eb25.png)
</details>

<details><summary>プロパティ値確認</summary>

```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](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/677104/8bf59138-e330-d483-04f6-9043ad41b204.png)
</details>

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

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

```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](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/677104/22b2a8c5-a836-2ab6-239d-a00b0072d0ca.png)

</details>

<details><summary>inputタグ ngModel プロパティ値更新されてるか確認</summary>

```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](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/677104/7371c179-05cb-8bf3-c2e0-c8e44c8abf2e.png)
</details>

<details><summary>メソッドの返り値確認</summary>

::: note
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](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/677104/087a4da4-bc84-f577-dea8-b091d121b6df.png)

::: note

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

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/677104/78a8f207-7a2d-eb10-a44e-67da024e645d.png)


:::


</details>

<details><summary>メソッド処理でプロパティ値が正しく変更更新されてるか確認</summary>

```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](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/677104/4271277e-6f04-b3e8-a011-d9b0c3270acc.png)

:::note
追加

methodTestメソッドをonChangeメソッドから呼ぶようにします。
onChangeメソッドはHTML側の<button (click)="onChange()">が定義されています。

```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](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/677104/e8303a79-56d8-a19c-90cc-86d65d47b781.png)


:::
</details>

<details><summary>画面Xから画面Yへ正しく遷移できてるか確認</summary>

```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');
  })

```

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

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

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

:::

</details>

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

■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](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/677104/4e2d2f4f-50b6-ff16-8206-d9bf56492c53.png)



</details>

<!--CASE end-->