Search code examples
node.jstapfastify

How to mock function using node-tap on fastify inject


I want to make 100% coverage on this function with node-tap but I can't mock the error part, it always throw

Cannot find module 'create' Require stack: - /home/mptr8/Code/Projects/me/fastify-example/fastify-postgres/test/integration.js

But I have create function on my query.js file, what do I do wrong here? Why it doesn't invoke the method?

    t.mock("../query.js", {
      create: () => {
        throw new Error();
      },
    });

I also try this combination, because query.js are dependent on db.js. Now the mock error gone but still I'm not getting the error throw from my fastify.inject.

    t.mock("../db.js", {
      "../query.js": {
        create: () => { throw new Error() },
      },
    });

     app.post("/", async (request, reply) => {
    try {
      const { body } = request;
      const book = create(body.title);
      reply.send(book);
    } catch (error) {
      // this part are not covered
      reply.code(500).send({ message: "internal server error" });
    }
  });


here are my complete code. You can see the full code on this github repository.

// server.js

const fastify = require("fastify");

const {
  migration,
  create,
} = require("./query");

const db = require("./db");

function build(opts = {}) {
  const app = fastify(opts);

  migration();

  app.post("/", async (request, reply) => {
    try {
      const { body } = request;
      const book = create(body.title);
      reply.send(book);
    } catch (error) {
      reply.code(500).send({ message: "internal server error" });
    }
  });

  app.addHook("onClose", (_instance, done) => {
    db.close();
    done();
  });
}

module.exports = build;
// db.js
const { Pool } = require("pg");
const pool = new Pool({
  connectionString:
    "postgresql://postgres:postgres@localhost:5432/fastify_postgres?schema=public",
});

module.exports = {
  query: (text, params) => pool.query(text, params),
  close: () => pool.end(),
};
// query.js
const db = require("./db");

async function migration() {
  await db.query(`
    CREATE TABLE IF NOT EXISTS books (
      id serial PRIMARY KEY,
      title varchar (255) NOT NULL
    )
  `);
}

async function create(title) {
  return await db.query("INSERT INTO books (title) VALUES ($1)", [title]);
}

module.exports = { migration, create };
// test.js
const tap = require("tap");
const fastify = require("../server");

tap.test("coba", async (t) => {
  const app = await fastify();
  t.test("should success create books", async (t) => {
    const response = await app.inject({
      method: "POST",
      url: "/",
      payload: {
        title: "Hello,World!",
      },
    });
    t.equal(response.statusCode, 200);
  });

  t.test("should throw error", async (t) => {
    const app = await fastify();
    // it doesn't throw the error :((
    t.mock("../query.js", {
      create: () => {
        throw new Error();
      },
    });
    const response = await app.inject({
      method: "POST",
      url: "/",
      payload: {
        title: "Hello,World!",
      },
    });
    t.equal(response.statusCode, 500);
    // call app close on last test child to close app and db properly
    app.close();
  });
});


Solution

  • You should use the returned value by the t.mock function:

    const build = t.mock({ 
      "../server": {
        "./query.js": {
          create: () => { throw new Error() },
        }
      }
    })
    const app = await build({})