VitestでMariaDBのqueryメソッドをモックする
Vitest[1]でMariaDB[2]のqueryメソッドをモックする際に、少し躓いたので、その躓き点と解決策を共有しようと思います。
テスト初心者なので、何か間違いがあれば、ご指摘いただけると幸いです。
モックする方針
Jestのモック方法[3]のように、__mocks__
にモックオブジェクトを入れてする方法があるかと思います。今回は、query
で発生したエラーが、呼び出すメソッドでキャッチできるかを試したかっただけなので、spyOn
を使って簡単に実現したいと思いました(結果的に、こちらのほうが、とても時間がかかってしまったのですが…)。
テスト対象のメソッド
以下のコード内のcheckCardName
メソッドでquery
がエラーを吐いたときに、checkCardName
メソッドを呼び出したところでエラーを受け取れるかテストしたいと思います。
import mariadb, { Connection, Pool } from "mariadb";
export default class OperateMariadb {
pool: Pool;
connection: Connection | undefined;
constructor() {
this.pool = mariadb.createPool({
host: process.env.MARIADB_HOST,
user: process.env.MARIADB_USER,
password: process.env.MARIADB_PASSWORD,
database: process.env.MARIADB_DATABASE,
});
}
async getConnection(): Promise<void> {
try {
this.connection = await this.pool.getConnection();
} catch (err: any) {
throw new Error(err);
}
}
async disconnection(): Promise<boolean> {
if (this.connection) {
try {
await this.connection.end()
return true;
} catch (err: any) {
throw new Error(err);
}
} else {
throw new Error("The connection has not been established.")
}
}
poolEnd() {
this.pool.end();
}
// このメソッドをテストしたい
async checkCardName(cardName: string): Promise<boolean> {
try {
if (this.connection) {
const result = await this.connection.query(
"select * from cards where card_name = ?",
[cardName]
);
delete result.meta;
const lengthObj = Object.keys(result).length;
if (lengthObj > 0) {
return true;
} else if (lengthObj === 0) {
return false;
} else {
throw new Error("Object length is invalid in checkCardName.");
}
} else {
throw new Error("The database connection does not exist.");
}
} catch (err: any) {
throw new Error(err);
}
}
}
とりあえず、書いてみた(動くけど、良くないコード)
とりあえず、自分が思ったように書いてみました。
it("con.query throws error", async () => {
const operateMariadb = new OperateMariadb();
await operateMariadb.getConnection();
vi.spyOn(operateMariadb.connection, "query").mockImplementation(() =>
Promise.reject(new Error("Query Error."))
);
expect(operateMariadb.checkCardName("king")).rejects.toThrow(
"Query Error."
);
operateMariadb.disconnection();
operateMariadb.poolEnd();
});
checkCardName
内でqueryを呼び出すときはawait this.connection.query("select * from cards where card_name = ?",[cardName]);
ですが、これをspyOn
でモックする方法で少し悩みました。
このコードは良くなさそうです。なぜなら、これをエディタで表示すると以下の画像のようにエラーが出ます。
しかし、このエラーを無視してテストしても、テストは通ります。
エラーが出る箇所
エラーの詳細
何が悪いのか
Vitestのエラーは簡単で、よく分からなかったので、Jestに突っ込んでみました。すると、以下のようなエラーが出ます。
なんだか、operateMariadb.connection
がundefined
になるのが悪いって言ってそうです。
このエラーの前にif (operateMariadb.connection)
を入れると、エラーが無く動きました。
最終に出来たコードは以下になります。
describe("Error handling testing.", () => {
it("con.query throws error", async () => {
const operateMariadb = new OperateMariadb();
await operateMariadb.getConnection();
if (operateMariadb.connection) { //これがないと、queryでエラーが出る
vi.spyOn(operateMariadb.connection, "query").mockImplementation(() =>
Promise.reject(new Error("Query Error."))
);
expect(operateMariadb.checkCardName("king")).rejects.toThrow(
"Query Error."
);
operateMariadb.poolEnd();
}
});
it("this.connection is undefined.", () => {
const operateMariadb = new OperateMariadb();
expect(operateMariadb.checkCardName("king")).rejects.toThrow(
"The database connection does not exist."
);
operateMariadb.poolEnd();
});
});
このコードの実行結果は、以下のGithub Actionsでも見ることが出来ます。
-
Vitest / Vitest https://vitest.dev/ ↩︎
-
Node.js 用 MariaDB Connector / MariaDB https://mariadb.com/ja/resources/blog/node-js-connector/ ↩︎
-
ES6 クラスのモック / Jest https://jestjs.io/ja/docs/es6-class-mocks ↩︎
Discussion