0

I wanna test my route. The problem is that I can't mock "multer" multiple times with different content. My Test:

import request from "supertest";
import express from "express";
import importRouter from "../routes/import.route"; // Deine Route importieren
import { vi, describe, it, expect, beforeEach } from "vitest";

let counter = 0;

beforeEach(() => {
  // Mocke multer und füge diskStorage statisch hinzu
  vi.mock("multer", () => {
    // Erstelle single und diskStorage innerhalb der Mock-Funktion
    const single = vi.fn(
      (fieldname: any) => (req: any, res: any, next: any) => {
        req.file = {
          originalname: "test.csv",
          size: counter === 2 ? 100 : 600 * 1024, // Simuliere Dateigröße
          path: "uploads/test.csv",
        };

        const error: any =
          counter === 2
            ? new Error("Unknown error")
            : new Error("File too large");
        error.code = counter === 2 ? "UNKNOWN_ERROR" : "LIMIT_FILE_SIZE";
        next(error); // Simuliere Dateigrößenfehler
      }
    );

    const diskStorage = vi.fn(() => ({
      destination: vi.fn((req, file, cb) => cb(null, "uploads")),
      filename: vi.fn((req, file, cb) => cb(null, file.originalname)),
    }));

    const multerMock = () => ({
      single,
      diskStorage: diskStorage(),
    });

    multerMock.diskStorage = diskStorage;

    return {
      default: multerMock,
      diskStorage,
    };
  });

  counter++;
});

describe("POST /import/customers", () => {
  it("should return 400 if file size exceeds limit (512KB)", async () => {
    let app = express();
    app.use(express.json());
    app.use("/import", importRouter);

    // Verwende Supertest, um die Route zu testen
    const res = await request(app)
      .post("/import/customers")
      .attach("file", Buffer.from("Some content"), "test.csv");

    expect(res.status).toBe(400);
    expect(res.body.msg).toBe("File too large. Maximum size is 512KB.");
  });

  it("should return 500 for general file upload error", async () => {
    let app = express();
    app.use(express.json());
    app.use("/import", importRouter);

    const res = await request(app)
      .post("/import/customers")
      .attach("file", Buffer.from("Some content"), "test.csv");

    expect(res.status).toBe(500);
    expect(res.body.msg).toBe("File upload error");
  });
});

And the file:

import { Router } from "express";
import multer from "multer";
import { importCustomers } from "../controllers/import.controller";
import * as path from "path";

const router = Router();
// Multer configuration for file uploads
const storage = multer.diskStorage({
  destination: function (req, file, cb) {
    const uploadPath = path.resolve(__dirname, "..", "..", "uploads");
    cb(null, uploadPath);
  },
  filename: function (req, file, cb) {
    cb(null, file.originalname);
  },
});
const upload = multer({
  storage,
  limits: { fileSize: 512 * 1024 }, // 512KB in Bytes
}).single("file");

/**
 * @route POST /import/customers
 * @desc Import customers from a CSV file
 */
router.post(
  "/customers",
  (req, res, next) => {
    upload(req, res, (err) => {
      if (err) {
        if (err.code === "LIMIT_FILE_SIZE") {
          return res
            .status(400)
            .json({ msg: "File too large. Maximum size is 512KB." });
        }
        return res
          .status(500)
          .json({ msg: "File upload error", error: err.message });
      }
      next(); // Go to next controller
    });
  },
  importCustomers
);

export default router;

This test is working as well. But i don't wanna use a "counter" value to handle the different body/content of the multer functions. So how can I do it in a common way?

1 Answer 1

2

You can set context dynamically with mock, I believe. NOTE: Haven't tested it, just took something we use in our tests and applied:

import request from "supertest";
import express from "express";
import importRouter from "../routes/import.route";
import { vi, describe, it, expect, beforeEach } from "vitest";

beforeEach(() => {
  // Reset mocks before each test
  vi.resetModules();

  // Mock multer
  vi.mock("multer", () => {
    const single = vi.fn(
      (fieldname: any) => (req: any, res: any, next: any) => {
        req.file = {
          originalname: "test.csv",
          size: 600 * 1024, // Default size
          path: "uploads/test.csv",
        };

        // Check if a custom error is set on the request object for testing purposes
        if (req.mockError) {
          const error: any = new Error(req.mockError.message);
          error.code = req.mockError.code;
          return next(error);
        }

        next();
      }
    );

    const diskStorage = vi.fn(() => ({
      destination: vi.fn((req, file, cb) => cb(null, "uploads")),
      filename: vi.fn((req, file, cb) => cb(null, file.originalname)),
    }));

    const multerMock = () => ({
      single,
      diskStorage: diskStorage(),
    });

    multerMock.diskStorage = diskStorage;

    return {
      default: multerMock,
      diskStorage,
    };
  });
});

describe("POST /import/customers", () => {
  it("should return 400 if file size exceeds limit (512KB)", async () => {
    let app = express();
    app.use(express.json());
    app.use("/import", importRouter);

    // Mock a file size limit error for this test case
    const res = await request(app)
      .post("/import/customers")
      .attach("file", Buffer.from("Some content"), "test.csv")
      .set("mock-error-code", "LIMIT_FILE_SIZE")
      .set("mock-error-message", "File too large");

    expect(res.status).toBe(400);
    expect(res.body.msg).toBe("File too large. Maximum size is 512KB.");
  });

  it("should return 500 for general file upload error", async () => {
    let app = express();
    app.use(express.json());
    app.use("/import", importRouter);

    // Mock a general error for this test case
    const res = await request(app)
      .post("/import/customers")
      .attach("file", Buffer.from("Some content"), "test.csv")
      .set("mock-error-code", "UNKNOWN_ERROR")
      .set("mock-error-message", "Unknown error");

    expect(res.status).toBe(500);
    expect(res.body.msg).toBe("File upload error");
  });
});
Sign up to request clarification or add additional context in comments.

2 Comments

Using custom headers to handle it works, yes. But is this the right way?
Well, depends on the definition of what is right :) This would be "a right-er" way to do, as you leverage library functionality instead, in that sense. And makes code cleaner. But it is not necessarily meets the "completely right" definition.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.