Angular CLI で生成した雛形から、非常に初歩的なテスト(<li>)を作ってみる - Angular8

## 概要
Angular CLI で生成した雛形から、非常に初歩的なテストを作成したメモです。

**機能**

* 配列をリスト化して表示するだけのページ
* ng test (Jasmin + Karma)で、配列の内容が表示されるかを確認

**Point**
テストコード自体のポイントは次の点です。

* コンポーネントを追加した場合に *.spec.ts を修正する
* &lt;li&gt;～&lt;/li&gt; を取ってくるとき、HTMLCollection の型となるので、それを踏まえた書き方にする


**補足**

* Git Bash を使っているので、cmd.exe や PowerShell 派の人は読み替えが必要な箇所があるかもしれません

### 画面例
今回のテストでは、 foo/bar/baz のリストになっていることを確認することが主旨です。
![list-view-static-chrome.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/326797/11d73925-003f-527f-f989-6690007c6218.png)


## 環境

| | |
|---------|------|
| OS | win32 x64
| Angular | 8.2.12
| Angular CLI | 8.3.14
| Node | 11.13.0

## 手順

### 1. 前準備

#### 1.1 Angular CLI のインストール (optional)

Angular CLI をインストールします。
すでにインストール済みだったり、最新を追うだけの人はやらなくても良いです。

```
npm uninstall -g @angular/cli
npm install -g @angular/cli
```
**参考**
Angular CLI で古いバージョンを指定すると、package.json も Angular CLIの該当バージョン時点のものが生成されます。
Angular 自体、バージョンアップのサイクルが早い一方、非互換も多く生み出す傾向にあって、
Webで検索したサンプルコードが最新バージョンでそのまま動かなかったりとかよくあります。
そういう意味で、Angular CLI で `ng new` とか使う場合は特に、バージョンを意識すると結果的に時間の節約になる気がしています。

### 2. アプリケーションの雛形ファイルを作成する
以下のように雛形のプロジェクトの動作が確認できます。

```bash
cd angular_workspace ※ 適当なアプリ置き場を指定
ng new lists-view-static --skipGit=true
cd lists-view-static
ng serve --open
```

### 3. 弄る
配列を一覧表示するだけのコンポーネントを作成します。

```
cd src/app
ng generate component list-view
```

**補足**

* app.module.ts など、追加したモジュールの import がちゃんと追加されるようです  
  なお、テスト周りのファイルは、コンポーネント追加に追従できていないようなので、一通り修正が終わった後に更新します。

#### 3.1. src/app/app.component.html
コード全文。長々しい雛形コードを一掃して、次のコードに置き換える。

```html
<app-list-view></app-list-view>
```

**補足**

* src/index.html に上のコードが埋め込まれる
* src/app/list-view/list-view.component.ts の selector にあわせた名前を入れる

#### 3.2. src/app/list-view/list-view.component.ts
`listItems` の行を追加する。

```typescript
import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'app-list-view',
  templateUrl: './list-view.component.html',
  styleUrls: ['./list-view.component.css']
})
export class ListViewComponent implements OnInit {
  listItems = [ 'foo', 'bar', 'baz'];
  constructor() { }

  ngOnInit() {
  }

}
```

#### 3.3. src/app/list-view/list-view.component.html
配列を一覧する。

```html
<h2>List items</h2>

<ul>
  <li *ngFor="let item of listItems">{{item}}</li>
</ul>
```

### 4. 動作確認
以下の ng serve コマンドを実行する。ブラウザが開いてページが表示されます。

```bash
ng serve --open
```
### 5. テスト用にコードを弄る
弄る前に NG になることを試す。テストコードを弄った後、テストが通ることを確認しよう。

```bash
ng test
```
なお、一通り直し終えたら Karma の実行結果はこうなる。
<details>
![angluar_list_items_test_ok.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/326797/e8c9ea47-2dc2-e134-65e2-c9cdd6a2c3ad.png)
</details>


#### 5.1. src/app/app.component.spec.ts
htmlなど弄りまくっているので、コンポーネントのテストコードも全面的に置き換える。`ListViewComponent` の部分を足すことで、コンポーネントがみつからない類のエラーはなくなる。

<details><summary>サンプルコード</summary><div>

```typescript
import { TestBed, async } from '@angular/core/testing';
import { AppComponent } from './app.component';
import { ListViewComponent } from './list-view/list-view.component';

describe('AppComponent', () => {
  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [
        AppComponent, ListViewComponent ← ここ
      ],
    }).compileComponents();
  }));

  it('should create the app', () => {
    const fixture = TestBed.createComponent(AppComponent);
    const app = fixture.debugElement.componentInstance;
    expect(app).toBeTruthy();
  });
});
```
</div></details>

一応、上記の修正がない場合の Karma の実行結果を載せておきます。
![angluar_list_items_test_ng01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/326797/d2f407f6-dfc8-5ede-6d8c-676ca6af3d64.png)

#### 5.2 src/app/list-view/list-view.component.spec.ts
コード全体を掲載する。
初心者・・・だとちょっと行が多いと感じ、読む気がうせるかもしれない。
雛形から弄っているので、実際は `should list created` あたりが主な追加コード。

ポイントは次の点

* `should list created` のブロックは  
  list-view.component.ts の listItems の変数を覗いている
* `chack native elements` のブロックは  
  ブラウザの向けに生成された HTMLコードの内容を確認している

<details><summary>サンプルコード</summary><div>

```typescript
import { async, ComponentFixture, TestBed } from '@angular/core/testing';

import { ListViewComponent } from './list-view.component';

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

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [ ListViewComponent ]
    })
    .compileComponents();
  }));

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

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

  it('should list created', () => {
    expect(component.listItems[0]).toEqual('foo');
  });

  it('chack native elements', () => {
    const listElements: HTMLCollection = fixture.nativeElement.getElementsByTagName('li');
    expect(listElements[0].textContent).toEqual('foo');
    expect(listElements[1].textContent).toEqual('bar');
    expect(listElements[2].textContent).toEqual('baz');
  });
});
```
</div></details>

## 参考

* [Angular - ng serve](https://angular.jp/cli/serve)  
  `--open` は `Opens the url in default browser.` という意味。
* [Angular - テスト](https://angular.jp/guide/testing)
* [要素の取得方法まとめ - Qiita](https://qiita.com/amamamaou/items/25e8b4e1b41c8d3211f4)
* [【Angular6】Jasmine + Karma で単体テストを書いてみた ｜ DevelopersIO](https://dev.classmethod.jp/testing/unittesting/angular6-jasmine-karma-unittest/)

## あとがき

* nativeElement は、debugElement経由で取得したほうがよいのですが、今回はそうしなくても動くので敢えてそうしていない  
  次なるステップとしては、Angular のテストのページを見てください
* テスト周りのチュートリアルは、(応用面を重視してか)やや重量級なものが多いので、この記事ではチュートリアル的なレベルで作成してみました (需要があるかは不明)