I realise that this is quite late but I just wanted to share my solution with using jest, since I wasn't entirely satisfied with the solutions found here. I can't say my solution is very elegant and may just be hiding some code smell as I'm still learning TDD, but it works.
In my work I often want to log to a file specified via a winston.transports.File(filename: "<filename>")
transport. Let's say my log file is info.log
Of course, during testing, I don't want
- logs to be written to this
info.log
info.log
to be created if it doesn't exist.
This is so to avoid side-effects. The answers above along with mocking were enough for avoiding 1. but for some reason did not avoid 2. (explained why below) .
The way I set up my projects is usually as such
src
├── app.js
├── services
│ ├── logging
│ │ ├── logger.js
│ │ └── logger_utils.js
│ ├── foo.js
│ ├── bar.js
│ └── etc.js
├── tests
│ ├── foo.test.js
│ ├── bar.test.js
│ └── etc.test.js
└── logs
└── info.log
Focus mostly on the log-related files. logger.js
is where I instantiate and subsequently export the winston Logger object. I then write helper functions in logger_utils.js
for modularity and easier testing.
When my issue was appearing, logger.js
consisted in
problematic_logger.js
const winston = require("winston");
const path = require("path");
// define the log file directory
const log_dir = path.resolve(__dirname, "./../../logs/info.log");
// create logger
const logger = winston.createLogger({
transports: [
new winston.transports.File({
filename: log_dir
})
]
});
// export it
module.exports = logger;
I then required it in logger_utils.js
which would in turn be required in any other modules scripts. So, in testing (apart from testing logger_utils.js
), I only need to mock functions contained in logger_utils.js
, with no need to worry about logger.js
, since it is only called by logger_utils.js
.
Now, I'm not entirely sure about this, but I think 2. defined above still failed despite the mocks and the silencing because winston.createLogger()
was still being called, and I believe this will create a file even when a --silent flag is set. I don't know if this is true, but nevertheless the solutions above weren't working.
So, (inspired by this answer) what I decided to do is to simply not create any winston object when testing. I did this by changing my logger.js
file to
fixed_logger.js
const winston = require("winston");
const path = require("path");
// define the log file directory
const log_dir = path.resolve(__dirname, "../../logs/info.log");
// if we are testing, don't create any winston object
if (process.env.NODE_ENV === "test") {
// export
module.exports = {};
} else {
// behave normally otherwise
// create winston logger
const logger = winston.createLogger({
transports: [
new winston.transports.File({
filename: log_dir
})
]
});
// export it
module.exports = logger;
}
(NODE_ENV
is automatically set to "test" when running npm test
or npm run test:watch
etc.)
We still need to export something for logger_utils.js to not break when testing it, so we export an empty object. This is fine since it will be mocked.
Anyway, that's my first answer on stackoverflow out of the way. I hope it wasn't too disastrous, let me know if anyone wants further details.