配列を仮想DBとした掲示板の作成（2/5）:GETリクエスト・サーバ起動編

# GitHub

https://github.com/atlansien/js_excercise_for_backend_7

# 前提条件

[配列を仮想DBとした掲示板APIサーバの作成（1/5）:はじめに・環境構築・DB、コメントモデルの作成
](https://qiita.com/atlansien/items/43f778e1796a9b7a6ab0)


# 実装する機能

`「GET/api/comments」`リクエストを送ると、`コメント一覧`がレスポンス値として返ってくる


# この記事のまとめ
- DB内のコメントを会得するメソッドを作成した
- GETリクエストした時のメソッドを作成した。


# コメント一覧を返すメソッド（findAll）

```js:Comment.js
module.exports = {
  findAll: () => {
    return comments;
  }
};
```

`module.exports`内に`comments`のデータを返すメソッドを作成します。これで外にコメント一覧を返すことができます。


それでは、この`findAll`メソッドがきちんと働いているかテストしてみます。

## `findAll`のテスト

まずは、テストに必要なライブラリのインストールを行います。

- [mocha](https://mochajs.org/):テスト用のフレームワークです。

`mocha`は`test`ディレクトリ直下にあるjsファイルを自動で実行してくれますが、APIリクエスト用とモデル用のテストファイルはディレクトリ内で分けようと思っているので、`mocha.opts`という設定用ファイルを`test`ディレクトリ内に作成し、以下のオプションを設定します。

```:mocha.opts
--recursive
```
これで、testディレクトリ内でさらにディレクトリを作成、ファイルを分けてもテストが実行されるようになりました。

それと、`eslint`も、そのままだと`mocha`に依存しているライブラリが`not defined`となるので、以下のコマンドを追加します。

```:.eelintrc
"env": {
  "mocha": true
},
```

- [power-assert](https://github.com/power-assert-js/power-assert):値を比較するためのアサーションライブラリです。基本的にはテストフレームワークと併用します。

### テストの内容

今回のテストで知りたい内容は

1. `findAll`はメソッドである
2. `findAll`で返されるデータは配列である
3. `findAll`実行後、配列内に適切にデータが格納されている

この3点を確認するコードを記述します。

```js:findAll.test.js
const assert = require('power-assert');
const Comment = require('../../models/Comment');

describe('comment.findAllのテスト', () => {
  const comments = Comment.findAll();
  // describe内に以下のテストコードを記述していきます
});
```

`describe`,`it`:mochaのテスト用メソッドです、こちらのメソッド内にテストコードを記述します。

1. `findAll`はメソッドである

```js
const comments = Comment.findAll();
it('findAllはメソッドである', () => {
  assert.equal(typeof Comment.findAll, 'function');
});
```

`typeof`演算子で`findAll`がメソッド、関数オブジェクトであるかどうかテストします。
ここで`assert`を使い、第一引数と第二引数が等しいかどうか判別できます

2. `findAll`で返されるデータは配列である

```js
it('commentsは配列である', () => {
  assert.equal(Array.isArray(comments), true);
});
```

`Array.isArray()`メソッドで、`findAll`で返されたデータが配列であるかテストします。
配列であるなら`true`が返ってくるはずなので、第二引数に`true`を入れます


3. `findAll`実行後、配列内に適切にデータが格納されている

```js
it('commentsには適切にデータが格納されている', () => {
  comments.forEach(comment => {
    assert.deepEqual(comment, {
      id: comment.id,
      username: comment.username,
      body: comment.body,
      createdAt: comment.createdAt,
      updatedAt: comment.updatedAt,
    });
  });
});
```

`assert.deepEqual()`メソッドで、`comment`クラス内部のプロパティ値は適切かどうかをテストします（`id`プロパティに`body`の値が入っていたりするとよくないので）

これらのテストがすべて成功することを確認したら、次はAPIリクエストをした時に、コメント一覧が返ってくるようコーディングします。


# comments.js(controller)の作成
MVC構想での**C**ontrollerの機能を司るファイルです。ここで書いたメソッドは後述するルーティング用ファイル経由で実行します。

# GETリクエスト時に動作するメソッドの作成（getComment()）

```js:comments.js(controller)
const Comment = require('../models/Comment');

module.exports = {
  getComment: (req, res) => {
    const storedComments = Comment.findAll();

    res.status(200).json(storedComments);
  },
};
```
`Comment.findAll()`を実行後、得たデータをjson形式で渡します、その際にステータスコード`200`（通信成功）も一緒に渡します。

# comments.js(routers)の作成
こちらでControllerのメソッドを読み込み、各HTTPリクエストが行われた際の紐付けを行います

```js:comments.js(routers)
const express = require('express');
const router = express.Router();
const controller = require('../controller/comments');

router.route('/').get(controller.getComment);

module.exports = router;
```

- GETリクエストが来た場合の動作の実装
  Controller.getCommentメソッドを実行します、実行した際のデータは`module.exports`で外部に渡します
  
# app.jsの作成

```js:app.js
const express = require('express');
const router = require('./routers/comments');

const app = express();

app.use('/api/comments', router);

module.exports = app;
```

`PORT/api/comments`アドレスにアクセスした際の動きを記述していますが、こちらのファイルで直接実行せず、後述の`index.js`でサーバー起動を行いますので（1つの機能ごとにファイルを分けられるなら分けていったほうが後々のコード確認や修正も行いやすいので）`module.exports`で外に渡します

# index.jsの作成

こちらでポート番号を指定後、`app.js`から渡されたデータを読み込み、サーバ起動を行います。

```js:index.js
const app = require('./app');

const PORT = 8050;

app.listen(PORT, function() {
  console.log(`Example app listening on port ${PORT}`);
});
```

## サーバー起動し、動作は正常か確認する

nodemon経由でindex.jsの起動後、ブラウザから`8050/api/comments`にアクセスします。

[nodemon](https://nodemon.io/):指定したファイルを更新するたびにサーバーを自動で再起動してくれるライブラリです

<img width="448" alt="スクリーンショット 2019-05-11 10.02.57.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/349049/de5038d3-5390-6127-337d-ea93a2e7ccee.png">


コメント一覧がjson形式で表示されたのでGETリクエストした際の実装は成功しました。

次に、本当にサーバー側での挙動は問題ないかテストを行いたいと思います。

# getComment()のテスト

まずは、テストに必要なライブラリのインストールを行います。

- [supertest](https://github.com/visionmedia/supertest):APIの挙動の確認をテストできるライブラリです

## テストの内容

1. `api/todos`からGETリクエストを行なった際に、json形式でデータを渡している
2. jsonデータの内部は適切である

この3点を確認するコードを記述していきます

```js:getComment.test.js
const request = require('supertest');
const assert = require('power-assert');

const app = require('../../../app');

describe('test 「GET /api/comments」', () => {
  it('GETリクエストで返されたデータは適切である', async () => {
    //以下のテストコードを記述していきます
  });
});
```

1. `api/todos`からGETリクエストを行なった際に、json形式でデータを渡している
```js
const response = await request(app)
  .get('/api/comments')
  .expect('Content-Type', /json/)
  .expect(200);
```

`supertest`は内部で擬似的にサーバ起動を行います。
`get`リクエスト後、`.expect`でデータがjsonであることとステータスコードが`200`で渡されていることを確認します。

2. jsonデータの内部は適切である

```js
const comments = response.body;
assert.equal(Array.isArray(comments), true);
comments.forEach(comment => {
  assert.equal(typeof comment.id, 'number');
  assert.equal(typeof comment.username, 'string');
  assert.equal(typeof comment.body, 'string');
  assert.equal(typeof comment.createdAt, 'string');
  assert.equal(typeof comment.updatedAt, 'string');
});
```
json内部のデータは配列であることを確認し、配列に格納されているデータは適切かどうかチェックします。

これらのテストが完了したら、「`「GET/api/comments」`リクエストを送ると、`コメント一覧`がレスポンス値として返ってくる」の実装は完了です。

# 現在の構成

```:tree
.
├── README.md
├── app.js
├── controller
│   └── comments.js
├── index.js
├── models
│   └── Comment.js
├── package-lock.json
├── package.json
├── routers
│   └── comments.js
└── test
    ├── app
    │   └── api
    │       └── getComment.test.js
    ├── mocha.opts
    └── models
        └── findAll.test.js
```


次は「`「POST/api/comments」`リクエストを送ると、`新規にComment1件を作成し、作成されたComment`がレスポンス値として返ってくる」機能の実装です。

[配列を仮想DBとした掲示板の作成（3/5）:POSTリクエスト編](https://qiita.com/atlansien/items/9cf58d3b657b8fe72a74)