How to Mock query Method of MariaDB using spyOn on Vitest
Tags: typescript, mariadb, testing, vitest
Published: 2022-06-03 06:38:51

I faced a problem to mock `query` of [MariaDB](https://mariadb.com/resources/blog/node-js-connector/) on [Vitest](https://vitest.dev/). 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"](https://jestjs.io/docs/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`.

```ts
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);
    }
  }
}
```

# The Code on My Idea (This code is not good)
The following is my idea but it is not good.

```ts
  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();
  });
```

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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/brvmsxtd6cqeuean42bu.png)


![The detail of an error](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/inbivktbmzosedxe6gfu.png)

# 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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/14jqkoo7uiof06rtau7x.png)

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.

```ts

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();
  });
});
```

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](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.
{% embed https://zenn.dev/akira_kashihara/articles/4011d04356cdbd %}