配列を仮想DBとした掲示板の作成（5/5）:DELETEリクエスト編

# GitHub

https://github.com/atlansien/js_excercise_for_backend_7


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

[配列を仮想DBとした掲示板の作成（2/5）:GETリクエスト・サーバ起動編
](https://qiita.com/atlansien/items/7ee78f6d89c6ae3fbeae)

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

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

# 実装する機能

`「DELETE/api/comment/:id」`リクエストを送ると、`id値に紐づいたComment1件を削除して、削除したComment1件`がレスポンス値として返ってくる

# この記事のまとめ

- 指定したidと合致したcomment1件を削除して、そのコメントを返り値とするメソッドを作成した
- DELETEリクエストを送った時のメソッドを作成した
- メソッド名の変更を行なった
- POSTMANでDELETEリクエスト時の動きを確認した

# 指定したid値と合致したcomment1件を削除して、削除したコメント1件を返すメソッドの作成（`deleteComment()`）


```js
deleteComment: ({ id }) => {
  if (typeof id !== 'number' || id < 1) {
    throw new Error(
      'idに適切でない値が入っています、1以上の数字を入れてください'
    );
  }

  const target = comments.findIndex(comment => id === comment.id);
  if (target === -1) {
    throw new Error('idと合致するCommentが見つかりません');
  }
  const deletedComment = comments.splice(target, 1)[0];

  return deletedComment;
},
```
- `id`値に適切でないプロパティ値が入っている
- `id`値と合致するcommentが見つからない

上記の場合はエラーを返し、適切な値が入っている場合のみメソッドが成功するように設計しました。

また、idのプロパティ値と配列のインデックス値は同一ではないので、配列内からid値と合致する要素を探す必要があります、そこで`Array.prototype.findIndex()`を使用します。

```js
const target = comments.findIndex(comment => id === comment.id);
```

アロー関数の特徴である、文の戻り値を返すだけならブロックと`return`を省略した書き方をしています。
これで、id値と合致するコメントを配列内から探すことができます。

そして、コメントを一件削除して、そのコメントを返す必要があります。

```js
const deletedComment = comments.splice(target, 1)[0];

return deletedComment;
```

`Array.prototype.splice()`は破壊的なメソッドです、第一引数に先ほど会得した`target`、第二引数で削除したい配列の数`1`を入れることで、id値と合致するコメント1件が`comments`配列から削除されます。

ここで、`splice()`の末尾に`[0]`をつけてますが、これをつけることで、配列ではなく1件のデータとして要素を返すことができます。

詳しい説明はこちらで記事にしてます。
[【Javascript】splice()の末尾に[0]をつけ取り除かれた要素１つをデータで返す - じんのアウトプット日記（だいたい毎日更新）](https://proghallelujah.hatenablog.com/entry/2019/04/27/000822)

それでは、実際にうまく動作するかテストします。

## `deleteComment()`のテスト

### テスト内容

1. `Comment.deleteComment`はメソッドである
2. `id`の引数に不正な値が入っていた場合、エラーが返る
3. `id`の引数と合致する`Comment`がない場合、エラーが返る
4. 適切なデータを送った場合、`id`と合致する`Comment`1件が返ってくる
5. `4.`と同時に、`id`と合致する`Comment`1件が配列から削除される。

この5点を確認します。

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

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

1.`Comment.deleteComment`はメソッドである

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

`typeof`演算子で、`Comment.deleteComment`がメソッドであるかどうかテストします。

2.`id`の引数に不正な値が入っていた場合、エラーが返る

```js
it('idの引数に不正な値が入っていた場合、エラーが返る', () => {
  const invalidIdList = [
    { id: 0 },
    { id: -1 },
    { id: null },
    { id: {} },
    { id: [] },
    { id: '1' },
  ];

  invalidIdList.forEach(id => {
    try {
      Comment.deleteComment(id);
      assert.fail();
    } catch (error) {
      assert.strictEqual(
        error.message,
        'idに適切でない値が入っています、1以上の数字を入れてください'
      );
    }
  });
});
```

`invalidIdList`配列に思いつく限りの適切でない値を入れ、`forEach`で各値ごとに`deleteComment`を実行します。メソッドがエラーになるようであれば`catch`の方でエラーメッセージと比較します。  1つでも成功した場合は`assert.fail()`によりエラーで終了、失敗となります。

3.`id`の引数と合致する`Comment`がない場合、エラーが返る

```js
it('idのプロパティ値と合致するCommentがない場合、エラーが返る', () => {
  const invalidId = { id: 999999999 };

  try {
    Comment.deleteComment(invalidId);
    assert.fail();
  } catch (error) {
    assert.strictEqual(error.message, 'idと合致するCommentが見つかりません');
  }
});
```

`invalidId`に現在配列には入っていないid値を入れ、`deleteComment`を実行します。メソッドがエラーになるようであれば`catch`の方でエラーメッセージと比較します。成功した場合は`assert.fail()`によりエラーで終了、失敗となります。

4.適切なデータを送った場合、`id`と合致する`Comment`1件が返ってくる

```js
  it('適切なid値を送った場合、idと合致するComment一件が返される', () => {
    const validId = { id: 1 };

    const deletedComment = Comment.deleteComment(validId);
    assert.deepEqual(deletedComment, {
      id: validId.id,
      username: deletedComment.username,
      body: deletedComment.body,
      createdAt: deletedComment.createdAt,
      updatedAt: deletedComment.updatedAt,
    });
  });
```

`id`に適切な値が入っていた場合は、`deleteComment`が実行した時に、返ってくる要素の各プロパティ値には適切にデータが入っているか`assert.deepEqual`で確認します。

5.`4.`と同時に、`id`と合致する`Comment`1件が配列から削除される。

```js
it('適切なid値を送った場合、idと合致するComment一件が配列から削除される', () => {
  const oldComments = Comment.findAll();
  const validId = { id: 2 };

  Comment.deleteComment(validId);

  const currentComments = Comment.findAll();

  assert.equal(oldComments.length, currentComments.length + 1);
});
```

`id`に適切な値が入っていた場合は、`deleteComment`実行後、配列から要素が一件削除されているかテストします。

各テストが成功したら、次はAPIから`DELETE`リクエストをした時に、`deleteComment`が実行するようにしていきます。

# `「DELETE/api/comment/:id」`リクエストを送ると、`id値と合致したcomment1件を削除して、削除したcomment1件`がレスポンス値として返ってくるメソッドの作成(`deleteComment()`)

```js:comment.js(controller)
deleteComment: (req, res) => {
  try {
    const parsedId = parseInt(req.params.id, 10);

    const removeComment = Comment.removeComment({
      id: parsedId
    });

    res.status(200).json(removeComment);
  } catch (error) {
    res.status(400).json({ message: error.message });
  }
}
```

```js:comment.js(router)
router
  .route('/:id')
  .put(controller.putComment)
  .delete(controller.deleteComment);
```

今回も`PUT`リクエストと同じく`ルートパラメータ`を使用します。ルートパラメータで得た値を数値に変換し、`removeComment`のid値に代入します。
と、その前に突然現れた`removeComment`とはなんでしょうか？

## メソッド名の変更

今回`DELETE`リクエスト時に実行される`deleteComment`と、`指定したid値と合致したComment1件を削除して、削除したコメント1件を返すメソッド`にて、modelsで作成した`deleteComment`の名前が被ってしまいましたので、 modelsの方を`removeComment`と改名しました。

## `deleteComment()`のテスト

`test/app/api`ディレクトリに`deleteComment.test.js`ファイルを新たに作成し、そちらでテストを行なっていきます。

```js:deleteComment.test.js
const assert = require('power-assert');
const requestHelper = require('../../../helper/requestHelper');

const getComments = async () => {
  const response = await requestHelper.request({
    method: 'get',
    endPoint: '/api/comments',
    statusCode: 200,
  });
  return response.body;
};

const deleteComment = async (id, code) => {
  const response = await requestHelper.request({
    method: 'delete',
    endPoint: `/api/comments/${id}`,
    statusCode: code,
  });
  return response;
};

describe('TEST 「DELETE api/comments/:id」', () => {
  // 以下にテストコードを記述していきます
});
```

### テスト内容
1. 適切でない`id`を送ると`400`エラーが返る
2. 送られた`id`とひもづく`Comment`がない場合、400エラーが返る
3. 適切なデータを送った場合、`id`とひもづく`Comment`がレスポンス値として返ってくる。
4. `3.`と同時に、配列内にあったidとひもづく`Comment`は配列から削除される。

この４点を確認するテストコードを記述していきます。

1.適切でない`id`を送ると`400`エラーが返る

```js
it('適切でないid値を送るとエラーが返る', async () => {
  const response = await deleteComment(0, 400);

  assert.deepStrictEqual(response.body, {
    message: 'idに適切でない値が入っています、1以上の数字を入れてください',
  });
});
```

id値に0（適切でない数値）を入れると、ステータスコードは`400`、そしてエラーメッセージが返ってくることを確認します。

2.送られた`id`とひもづく`Comment`がない場合、400エラーが返る

```js
it('idの値と合致するCommentがない場合エラーが返る', async () => {
  const response = await deleteComment(9999999, 400);

  assert.deepStrictEqual(response.body, {
    message: 'idと合致するCommentが見つかりません',
  });
});
```

現在配列に入っているcommentのidのどれにも合致しない数字をidに入れると、ステータスコードは`400`、そしてエラーメッセージが返ってくることを確認します。


3.適切なデータを送った場合、`id`とひもづく`comment`1件がレスポンス値として返ってくる。
4.`3.`と同時に、配列内にあったidとひもづく`comment`1件は配列`comments`から削除される。

```js
it('適切なid値を送ると、idと合致するCommentが返ってくる、また該当のCommentは配列内から削除される', async () => {
  const oldComments = await getComments();

  const validId = 4;
  const response = await deleteComment(validId, 200);
  const comment = response.body;

  assert.deepStrictEqual(comment, {
    id: validId,
    username: comment.username,
    body: comment.body,
    createdAt: comment.createdAt,
    updatedAt: comment.updatedAt,
  });

  const currentComments = await getComments();

  assert.strictEqual(oldComments.length, currentComments.length + 1);
});
```

id値と合致するcommentがあった場合、合致したcomment1件、ステータスコード`200`が適切に返ってきていることを確認します。
それと同時に、`deleteComment`実行前と実行後の配列を比較して、comment1件が削除されていることを確認します。

以上のテストが全て正常に終了したら、今回の`APIサーバを用いた掲示板の作成`は全て終了です。

# 最終的な環境

```tree
.
├── Task_info.md
├── app.js
├── controllers
│   └── comments.js
├── helper
│   └── requestHelper.js
├── index.js
├── models
│   └── Comment.js
├── package-lock.json
├── package.json
├── readme.md
├── routers
│   └── comments.js
└── test
    ├── app
    │   └── api
    │       ├── deleteComment.test.js
    │       ├── getComment.test.js
    │       ├── postComment.test.js
    │       └── putComment.test.js
    ├── mocha.opts
    └── models
        ├── createComment.test.js
        ├── findAll.test.js
        ├── removeComment.test.js
        └── updateComment.test.js
```

# POSTMANを使用してDELETEリクエスト時の動きをチェックする(gif)
![DELETErequest.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/349049/23c6447b-d5cb-da35-d6a5-bba2ad4c8018.gif)