Jestを使ってexpressをTDDをしたい

# 事の発端

[TDD Boot Camp 2020 Online #1 基調講演/ライブコーディング](https://www.youtube.com/watch?v=Q-FJ3XmFlT8&t=5252s) を見てからTDDに興味津々です。
というか、長い間開発やってるのだから、この辺もちゃんと出来ないとだめだと思って勉強中です。

# 今回のターゲット

- 現在社内で構築しているbackendをTDDできるようにする

# 主な環境

- express
- jest
- typescript
- typeorm
- mariadb

一応、全部コンテナで実行できるようになっています。（ハズ）
[詳細はこちらを参照](https://github.com/shun/tdd-express-typescript/tree/0.1-tdd-no-graphql)

# 準備

自分は最近、npmでは無くyarn を使用しているので、以降すべてyarn を
使用することを前提に説明していきます。

ディレクトリの構成は主に以下のような感じです。

```
.
├── package.json
├── src
│   ├── entity
│   │   └── User.ts
│   ├── index.ts
│   ├── migration
│   └── server.ts
└── test
    └── server.spec.ts
```

## 必要なパッケージのインストール

多分、以下のパッケージを入れておけば良いのかと。
自分の場合は、ORMとしてtypeormを使っているので、typeormもインストールし
プロジェクトの作成もしています。

```bash
$ yarn global add typeorm
$ typeorm init --name app --database mysql
$ cd app
$ yarn add \
         @types/express \
         @types/express-session \
         @types/jest \
         @types/node \
         @types/supertest \
         @typescript-eslint/eslint-plugin \
         @typescript-eslint/parser \
         cors \
         eslint \
         eslint-config-prettier \
         eslint-plugin-prettier \
         express \
         express-session \
         jest \
         mysql2 \
         prettier \
         reflect-metadata \
         supertest \
         ts-jest \
         ts-node \
         tsconfig-paths \
         typescript
```

## express サーバの作成

まずはテストしやすいようにexpressのサーバを切り出します。

```typescript:server.ts
import express from "express";

export class AppServer {
  public app: express.Application;

  constructor() {
    this.app = express();
    this.setupRoutes();
  }

  public setupRoutes() {
    this.app.get("/", (req, res) => {
      res.json({ message: "AppServer started" });
    });
  }

  public start() {
    this.app.listen( {port: 4000}, () => {
      console.log(`server on http://localhost:4000`);
    });
  }
}
```

## package.json を修正

「yarn test」でテストを実行できるようにpackage.json を修正。

```diff:package.json
   "scripts": {
-     "start": "ts-node src/index.ts"
+     "start": "ts-node src/index.ts",
+     "test": "jest"
   },
```

jest の設定を何もしてないと以下のように「import」が使えないとエラーを
出すので、jestでtypescriptのソースを解釈できるように以下のように追記します。
自分の場合、テストコードは「test」ディレクトリに保存し、命名規則的には「〜.spec.ts」に
しているため、「testMatch」の部分にその旨を設定します。

```
Cannot use import statement outside a module
```

```diff:package.json
   "scripts": {
      "start": "ts-node src/index.ts",
      "test": "jest"
-   }
+   },
+   "jest": {
+     "moduleFileExtensions": [
+       "ts",
+       "js"
+     ],
+     "transform": {
+       "^.+\\.ts$": "ts-jest"
+     },
+     "globals": {
+       "ts-jest": {
+         "tsConfig": "tsconfig.json"
+       }
+     },
+     "testMatch": [
+       "**/test/**/*.spec.ts"
+     ]
+   }
```

## tsconfig.json を修正

「export default〜」で定義された定義をimportできるようにするため、
tsconfig.json に以下を追記します。
※ 認識あっているんだろうか？

```diff:tsconfig.json
{
   "compilerOptions": {
      "lib": [
         "es5",
         "es6"
      ],
      "target": "es5",
      "module": "commonjs",
      "moduleResolution": "node",
      "outDir": "./build",
      "emitDecoratorMetadata": true,
      "experimentalDecorators": true,
+      "esModuleInterop": true,
      "sourceMap": true
   }
}
```

ここまでで設定関連は終了のはず。

# テストソースを準備する

とりあえずbackend サーバとしては、GET で「/」にアクセスすると
「{message: "AppServer started"}」を返すようになっているため、
ステータスコード200を受けて、messageを正しく取得出来ているかのテストを準備します。

express をユニットテストする際には、どうもsupertest なるモジュールを使用するようなので、
こちらを使用します。
※ express のテストがこうだったし、他を探してもコレが一般的なのかと今の所思っています。

```typescript:server.spec.ts
import request from "supertest";
import { AppServer } from "../src/server";

describe("GET /", () => {
  it("return 200 and correct message", () => {
    const server = new AppServer();

    return request(server.app)
            .get("/")
            .expect(200)
            .expect({message: "AppServer started"});
  });
});
```

# テストの実行

「yarn test」を実行してテストがパスすることを確認します。
今回は全部がコンテナ内で動作できるようにしているため、
以下を実行すればできるはずです。


```bash
$ git clone git@github.com:shun/tdd-express-typescript.git
$ cd tdd-express-typescript
$ docker-compose pull # 必要に応じて
$ docker-compose up -d
$ docker-compose exec -u node express bash
node@b51cacc0699d:~/app$ yarn test
yarn run v1.22.4
warning package.json: No license field
$ jest
 PASS  test/server.spec.ts
  GET /
    ✓ return 200 and correct message (34 ms)

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        2.998 s
Ran all test suites.
Done in 3.71s.
```

これでやっとスタート地点に立てたかなと。。。
後は、これにtypeormでDBにアクセスしてデータを入れたり、取得したりすれば
思ったようなTDDができるはず。
これからTDDがんばるぞー。

長くなりましたが、ここまでお付き合いしていただきありがとうございます。