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?
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");
});
});