アジャイルコーチの備忘録

3歩歩いたら忘れるニワトリアジャイルコーチの備忘録。書評、活動記録など...

Discord BotをTypeScript/TDDで開発する(1)

最近、友人たちがDiscord Bot開発に興味を持ち、実際に開発しているのを見て、どうせだったら最近学習しているTypeScriptかつTDDで開発したいと思った矢先、[LIVE] Discord.js Unit Testing with Jest - YouTubeという素晴らしいYouTube動画を見つけたので、こちらの写経と動作確認を始めました。

まだ前半30分くらいですが、そこに至るまでのトラブルシューティングとつまづいたことをメモ、構成は以下の通り。

www.youtube.com

f:id:norihiko-saito-1219:20210711182032j:plain

ソースコード

// index.ts
import { Message } from "discord.js"

export const messageHandler = async (message: Message) => {
    if (message.content === '!agile') {
        message.channel.send("やさしい")
    }
}
// index.spec.ts //テストファイル
import { Message } from "discord.js";
import { messageHandler } from "../src/handlers";

describe('Message Handler', () => {
    const message = ({
        channel: {
            send: jest.fn(), // よくわからない(1)
        },
        content: '!agile',
    } as unknown) as Message; // よくわからない(2)

    it('should call message handler', async () => {
        messageHandler(message);
        expect(message.channel.send).toHaveBeenCalledWith("やさしい");
    });
});

トラブルシューティング

自分の環境では「sh: ts-node: command not found」 と「/bin/sh: jest: command not found」というエラーに遭遇しましたが、それぞれ単純に「yarn add -D ts-node」、「yarn add -D jest」コマンドで回避できました(単純にYouTubeを見落とした可能性あり)。

つまづいたこと

jest.fn()

jest.fn()が公式ドキュメント(モック関数 · Jest)を読んでもピンとこなかったのですが、以下ブログを読んでようやく理解できた気がしました。

medium.com

一言でいえば、jest.fn()とは関数のモックです。
コードで説明すると、index.tsのmessage.channel.send()の実行時に、index.spec.tsでmessage.channel.send()にアサインされたjest.fn()が実行されます。
ただし、jest.fn()は何の処理もない空の関数です(jest.fn()の処理を定義したい場合は「jest.fn(() => "bar"」のように記述)。
ただ、空の関数ということは問題ではありません。
ここで確認したいことはmessage.channel.send()が"やさしい"という引数で実行されているかどうかなので、index.spec.tsで「expect(message.channel.send).toHaveBeenCalledWith("やさしい")」と確認しています。

as unknown as Message

コードを読んでいまいち理解できなかったことは、どうしてindex.spec.tsでMessageをunknown経由でMessage型に型アサーションしているかどうか、です。
これは割とすぐに理解できました。
ここでやりたいことは、Discord.jsのMessageのMockを作ることですが、本来Discord.jsのMessageは沢山のプロパティを持っています。ですがMockで全てのプロパティを定義すると大変かつコード可視性が低下するため、Mockでは必要分のプロパティを定義し、それでもMessage型として扱いたいのでunknown経由でMessage型に型アサーションしているということでした。

テストのMockを作る際の、unknown経由の型アサーションは便利ですね(きっと定番なんだと思います)。

book.yyts.org

時間があれば、さらに環境のセットアップやさらに進めた時につまづきなどがメモできればと思います!