i have a single endpoint where i call twice on 2 different describes to test different responses
const express = require("express");
const router = express.Router();
const fs = require("fs");
const multer = require("multer");
const upload = multer({ dest: "files/" });
const csv = require("fast-csv");
let response = { message: "success" }
router.post("/post_file", upload.single("my_file"), (req, res) => {
let output = get_output(req.file.path);
fs.unlinkSync(req.file.path);
if(output.errors.length > 0) response.message = "errors found";
res.send(JSON.stringify(response))
})
const get_output = (path) => {
let errors = []
let fs_stream = fs.createReadStream(path);
let csv_stream = csv.parse().on("data", obj => {
if(!is_valid(obj)) errors.push(obj);
});
fs_stream.pipe(csv_stream);
return {errors};
}
const is_valid = (row) => {
console.log("validate row")
// i validate here and return a bool
}
my unit tests
const app = require("../server");
const supertest = require("supertest");
const req = supertest(app);
describe("parent describe", () => {
describe("first call", () => {
const file = "my path to file"
// this call succeeds
it("should succeed", async (done) => {
let res = await req
.post("/post_file")
.attach("my_file", file);
expect(JSON.parse(res.text).message).toBe("success")
done();
});
})
describe("second call", () => {
const file = "a different file"
// this is where the error starts
it("should succeed", async (done) => {
let res = await req
.post("/post_file")
.attach("my_file", file);
expect(JSON.parse(res.text).message).toBe("errors found")
done();
});
})
})
// csv file is this
NAME,ADDRESS,EMAIL
Steve Smith,35 Pollock St,[email protected]
I get the following
Cannot log after tests are done. Did you forget to wait for something async in your test? Attempted to log "validate row".
The problem is that tested route is incorrectly implemented, it works asynchronously but doesn't wait for get_output
to end and responds synchronously with wrong response. The test just reveals that console.log
is asynchronously called after test end.
Consistent use of promises is reliable way to guarantee the correct execution order. A stream needs to be promisified to be chained:
router.post("/post_file", upload.single("my_file"), async (req, res, next) => {
try {
let output = await get_output(req.file.path);
...
res.send(JSON.stringify(response))
} catch (err) {
next(err)
}
})
const get_output = (path) => {
let errors = []
let fs_stream = fs.createReadStream(path);
return new Promise((resolve, reject) => {
let csv_stream = csv.parse()
.on("data", obj => {...})
.on("error", reject)
.on("end", () => resolve({errors}))
});
}
async
and done
shouldn't be mixed in tests because they serve the same goal, this may result in test timeout if done
is unreachable:
it("should succeed", async () => {
let res = await req
.post("/post_file")
.attach("my_file", file);
expect(JSON.parse(res.text).message).toBe("success")
});