question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

How can you mock external dependencies?

See original GitHub issue

I am trying to test a logger-service, which uses imports from the node_modules winston and fs, and I would like to mock these imports, but I don’t know how. With nest it’s really easy to mock the injected dependencies, but these dependencies are imported, not injected. For normal Node.js modules I use proxyquire to proxy my required node_modules, but that’s not possible with Nest, because the dependencies are imported, not required, and they are imported very early in the process (before the constructor of the service is called).

Here is my logger-service:

import {Component} from '@nestjs/common';
import {Logger, transports} from 'winston';
import * as path from 'path';
import * as fs from 'fs';

import { ConfigService } from '../config/config.service';

require('winston-daily-rotate-file');

@Component()
export class LoggerService {
    logger:any;
    logFolders:any;

    constructor(private config:ConfigService) {
        this.logFolders = {base: path.join(__dirname, '..', '..', '..', 'logs')};
        this.logFolders.debug = path.join(this.logFolders.base, 'debug');
        this.logFolders.exception = path.join(this.logFolders.base, 'debug');

        this._createLogFolders();

        this.logger = new Logger({
            transports: [
                new transports.Console({
                    json: false,
                    timestamp: true,
                    level: config.get('debug') ? 'debug' : 'info'
                }),
                new transports.DailyRotateFile({
                    filename: path.join(this.logFolders.debug, 'debug.log'),
                    json: false,
                    level: 'error',
                    datePattern: 'yyyy-MM-dd-',
                    prepend: true
                })
            ],
            exceptionHandlers: [
                new transports.Console({
                    json: false,
                    timestamp: true,
                    level: config.get('debug') ? 'debug' : 'info'
                }),
                new transports.DailyRotateFile({
                    filename: path.join(this.logFolders.exception, 'execeptions.log'),
                    json: false,
                    level: 'error',
                    datePattern: 'yyyy-MM-dd-',
                    prepend: true
                })
            ],
            exitOnError: false
        });
    }

    private _createLogFolders() {
        const createDir = dir => {
            if (!fs.existsSync(dir)) {
                fs.mkdirSync(dir);
            }
        };

        createDir(this.logFolders.base);
        createDir(this.logFolders.debug);
        createDir(this.logFolders.exception);
    }

    debug(message:string) {
        this.logger.debug(message);
    }

    info(message:string) {
        this.logger.info(message);
    }

    warn(message:string) {
        this.logger.warn(message);
    }

    error(message:string) {
        this.logger.error(message);
    }
}

And this is my test:

import {Test} from '@nestjs/testing';
import {TestingModule} from '@nestjs/testing/testing-module';
import {LoggerService, ConfigService} from '../';
import {expect} from 'chai';
import * as sinon from 'sinon';

describe ('LoggerService', () => {
    let module: TestingModule;
    let service: LoggerService;
    let sandbox;
    let logStub;

    beforeEach(async () => {
        sandbox = sinon.sandbox.create();

        module = await Test.createTestingModule({
            components: [
                LoggerService,
                ConfigService
            ]
        }).compile();

        service = module.get(LoggerService);
    });

    it('should exist', () => {
        expect(service).to.exist;
    });

    it('should log a debug message', () => {
         logStub = sandbox.stub(service.logger, 'debug');
         service.debug('someMessage');
         expect(logStub).to.have.been.calledWith('someMessage');
    });

    it('should log a info message', () => {
        logStub = sandbox.stub(service.logger, 'info');
        service.info('someMessage');
        expect(logStub).to.have.been.calledWith('someMessage');
    });

    it('should log a debug message', () => {
        logStub = sandbox.stub(service.logger, 'warn');
        service.warn('someMessage');
        expect(logStub).to.have.been.calledWith('someMessage');
    });

    it('should log a debug message', () => {
        logStub = sandbox.stub(service.logger, 'error');
        service.error('someMessage');
        expect(logStub).to.have.been.calledWith('someMessage');
    });

});

I am not able to mock winston or fs, so I cannot test the constructor or prevent the creation of the logFolders.

Can anyone help me to mock imports?

Issue Analytics

  • State:closed
  • Created 6 years ago
  • Comments:15 (5 by maintainers)

github_iconTop GitHub Comments

3reactions
unlightcommented, Oct 19, 2017

You are not following IoC You must create injectable component from logger package and inject to constructor.

{
  provide: 'Logger',
  useFactory: () => require('winston')
};
constructor(
    private config:ConfigService,
   @Inject('Logger) private logger,
) {}
2reactions
unlightcommented, May 18, 2018

@mslourens You can create new type by using typeof (it behaves differently in TypeScript):

import * as fs from 'fs';

export class AppModule {

    constructor(
       @Inject('fs') private _fs: typeof fs
    ) { }
}

or

import * as fs from 'fs';
type FsType = typeof fs;

export class AppModule {

    constructor(
       @Inject('fs') private fs: FsType
    ) { }
}

Read more comments on GitHub >

github_iconTop Results From Across the Web

Isolating Dependencies in Tests Using Mocks and Stubs
Unit tests check on the behavior of units. Think of a class as being a unit. Classes, more often than not, have external...
Read more >
Unit Testing Best Practices When You Have External ...
Unit testing with external dependencies may be hard.
Read more >
How can I test/mock external code or external dependencies ...
I'm using Moq for mocking of my code in unit testing. It's ok to mock the code without external dependencies. Below code is...
Read more >
Mock External Dependencies via Traits - Catch the Dot
In this short blog post, I will give an example wherein we will see how with the help of Scala trait's we can...
Read more >
Using Mocks to Test External Dependencies or Reduce ...
So far we've used mocks to test external dependencies, like Django's mail-sending function. The main reason to use a mock was to isolate ......
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found