How to Mock query Method of MariaDB using spyOn on Vitest

akirakashihara

Akira Kashihara

Posted on June 3, 2022

How to Mock query Method of MariaDB using spyOn on Vitest

I faced a problem to mock query of MariaDB on Vitest. I share the problem and its solution here.
I appreciate that you point out my mistakes if you find them.

The Method to Be Test

You might have a way to mock query of MariaDB that is in the mocked class in __mocks__ directory according to "ES6 Class Mocks" on Jest official website. However, I want to quickly mock query method using spyOn because I just want to test whether the parent method can get the error query method throws.

The Method to Be Tested

This section indicates the method to be tested, which is called 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();
  }

  // This method to be tested.
  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);
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

The Code on My Idea (This code is not good)

The following is my idea but it is not good.

  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();
  });
Enter fullscreen mode Exit fullscreen mode

I'm not sure that this spyOn usage to mock await this.connection.query("select * from cards where card_name = ?",[cardName]); in checkCardName. It runs and passes correctly, but it is not good because it gets an error as the following.

The block has an error

The detail of an error

What's Wrong with This Code?

Vitest only gives us a brief error message, and I do not have any idea what's wrong with this code, so I run the testing on Jest. Jest gives us the following.

Jest Error

I guess Jest told me operateMariadb.connection can be undefined, which is wrong.
I insert if (operateMariadb.connection) before the error, and this testing runs without any errors.
The following is the final test code.


describe("Error handling testing.", () => {
  it("con.query throws error", async () => {
    const operateMariadb = new OperateMariadb();
    await operateMariadb.getConnection();
    if (operateMariadb.connection) { //Vitest and Jest throws an error on "query" in the line after this without this if statement.
      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();
  });
});
Enter fullscreen mode Exit fullscreen mode

You can check the execution result of this code in the following URL to the Github Actions page.

https://github.com/KASHIHARAAkira/vitest-playground/actions/runs/2432378503


This original article is the following that is written by me. This is a translation of a portion of this original article from Japanese to English.

💖 💪 🙅 🚩
akirakashihara
Akira Kashihara

Posted on June 3, 2022

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related