Как получить доступ к выводам Winston из Jest Test - PullRequest
0 голосов
/ 12 июля 2020

ОБНОВЛЕНИЕ: Я считаю, что нашел (возможно, хакерский) способ решения обеих проблем, завтра опубликую обновленный код и решения для всех, кого это интересует.

Я пытаюсь создать приложение CLI и решил, что хотел отформатировать вывод журнала для лучшего взаимодействия с пользователем, включая добавление подробного режима. Таким образом, я установил Winston и смог заставить его работать так, как я хотел. Затем я установил jest, поскольку приложение становится все более сложным, поэтому я хотел автоматизировать тестирование. Я решил сделать свой первый тест для регистратора, так как это было последнее, над чем я работал, и я сразу же столкнулся с проблемами. Мой план состоял в том, чтобы подсчитать строки вывода из журнала консоли и проверить, что они равны десяти: тогда это означало бы, что мне не пришлось бы жестко кодировать какие-либо сообщения об ошибках, указанные в c, которые могут измениться, если я решу изменить форматирование журнала позднее. Мне также нужен тест, который изменяет переменную среды с dev на prod, чтобы убедиться, что она правильно работает в обеих средах: кто-нибудь знает, возможно ли это? В настоящее время я использую dotenv для управления своими переменными среды и добавил соответствующий код в свой файл конфигурации jest, чтобы он правильно считывал переменные. Я читал различные сообщения о переполнении стека и шутливые документы о фиктивных функциях в попытке решить эти проблемы, но все это пролетает над моей головой. Мне не нужен этот тест, чтобы продолжить работу с моим приложением, поскольку я вполне уверен, что все работает нормально, но разочаровывает то, что не могу решить эти проблемы, и было бы полезно узнать об этом в будущем в случае мне действительно нужно сделать тест, который полагается на вывод журнала. Может ли кто-нибудь помочь?

set-log-to. js

const { createLogger, format, transports } = require('winston');
const { combine, printf, errors } = format;

const nodeEnvironment = process.env.NODE_ENV;

const cc = require('../constants/chalk-classes');
const {
  fatal,
  caveat,
  victory,
  error,
  warn,
  attempt,
  success,
  info,
  debug,
  plain,
} = cc;

const config = {
  levels: {
    fatal: 0,
    caveat: 0,
    victory: 0,
    error: 1,
    warn: 2,
    attempt: 3,
    success: 3,
    info: 3,
    verbose: 4,
    debug: 5,
  },
};

const formatting = combine(
  errors({ stack: true }),
  printf((infoObj) => {
    const { level, message } = infoObj;
    switch (level) {
      case 'fatal':
        return `${fatal(`${level}:`)} ${plain(message)}`;

      case 'caveat':
        return `${caveat(`${level}:`)} ${plain(message)}`;

      case 'victory':
        return `${victory(`${level}:`)} ${plain(message)}`;

      case 'error':
        return `${error(`${level}: ${message}`)}`;

      case 'warn':
        return `${warn(`${level}: ${message}`)}`;

      case 'attempt':
        return `${attempt(message)}`;

      case 'success':
        return `${success(message)}`;

      case 'info':
        return `${info(message)}`;

      case 'verbose':
        return `${plain(message)}`;

      case 'debug':
        return `${debug(level)}: ${plain(message)}`;
    }
  })
);

function setLevel(level) {
  if (!level) {
    if (nodeEnvironment === 'dev') {
      return (level = 'debug');
    } else {
      return (level = 'warn');
    }
  } else {
    return level;
  }
}

function setLogTo(level) {
  level = setLevel(level);
  const log = createLogger({
    levels: config.levels,
    level,
    transports: [
      new transports.Console({
        format: formatting,
      }),
    ],
  });
  return log;
}

module.exports = setLogTo;

set-log-to.test. js

const setLogTo = require('../set-log-to');

test('All log levels function correctly', () => {
  let log = setLogTo('debug');
  log.fatal('This is fatal');
  log.caveat('This is a caveat');
  log.victory('This is a victory');
  log.error('This is an error');
  log.warn('This is a warning');
  log.attempt('This is an attempt');
  log.success('This is a success');
  log.info('This is some info');
  log.verbose('This is verbose');
  log.debug('This is a debug');
  expect(???).toEqual(10);
});

test('Logger does not print debug as standard', () => {
  let log = setLogTo();
  log.warn('This is a warning');
  log.verbose('This is a verbose statement');
  log.debug('This is a debug statement');
  expect(???).toEqual(1);
});

test('Logger does not print info when set to error', () => {
  let log = setLogTo('error');
  log.info('This is an info statement');
  log.error('This is an error')
  expect(???).toEqual(1);
});

test('Dotenv works correctly', () => {
  let log = setLogTo();
  log.debug('This is a debug');
  nodeEnvironment = 'prod';
  log.debug('This us a debug');
  expect(???).toEqual(1)
});

1 Ответ

0 голосов
/ 14 июля 2020

Хорошо, значит, Winston не отправляет сообщения на консоль, а если отправляет, то на глобальную консоль. В любом случае я решил, что лучший способ обойти это - добавить регистратор файлов к регистратору консоли, впоследствии читая строки из файла, а не консоли, а затем удаляя файл после тестирования. Затем это вызвало проблемы, поскольку стандартное поведение Jest - запускать свой тест одновременно, поэтому каждый тест пытался получить доступ к одному и тому же временному файлу в одно и то же время. Это было легко исправить, передав параметр --runInBand для jest в моем файле package. json, который заставляет Jest запускать тесты последовательно.

Чтобы решить проблему переменной среды, оказалось, что Jest устанавливает Переменная NODE_ENV для «проверки» при запуске. Я добавил jest.resetModules () к оператору beforeEach, что позволило мне вручную объявлять среду в начале каждого теста. Я сохранил тестовую среду jest в константе в начале файла, чтобы я мог использовать ее для большинства тестов, которые не требовали явной установки переменных среды. Я также создал функцию afterAll (), чтобы убедиться, что process.env был возвращен в тестовую среду для любых последующих тестов.

Код:

package. json:

"logtest": "jest --runInBand ./src/helper/tests/set-log-to.test.js",

set-log-to.test. js:

const fs = require('fs');
const path = require('path');
const winston = require('winston');

let setLogTo = require('../set-log-to');

const { snipconDir } = require('../../constants/core-dirs');
const logTests = path.join(snipconDir, './temp/log-tests.txt');
const testEnv = process.env;

function createTestLog(level) {
  let log = setLogTo(level);
  log.add(
    new winston.transports.File({
      filename: logTests,
    })
  );
  return log;
}

function allLogLevels(log) {
  log.fatal('This is fatal');
  log.caveat('This is a caveat');
  log.victory('This is a victory');
  log.error('This is an error');
  log.warn('This is a warning');
  log.attempt('This is an attempt');
  log.success('This is a success');
  log.info('This is some info');
  log.verbose('This is verbose');
  log.debug('This is a debug');
}

async function getLines() {
  let data = await fs.promises.readFile(logTests);
  let lines = data.toString().split('\n');
  lines.pop();
  return lines;
}

beforeEach(async (done) => {
  jest.resetModules();
  try {
    await fs.promises.writeFile(logTests, '');
    done();
  } catch (err) {
    console.log(err);
  }
});

afterEach(async (done) => {
  try {
    await fs.promises.unlink(logTests);
    done();
  } catch (err) {
    console.error('Failed to delete log-tests.js');
  }
});

afterAll(() => {
  process.env = testEnv;
});

test('When level is explicitly set to debug, all messages show', async () => {
  process.env = testEnv;
  let log = createTestLog('debug');
  allLogLevels(log);
  let lines = await getLines();
  expect(lines.length).toEqual(10);
});

test('Standard behaviour shows messages from level 2 and below', async () => {
  process.env = testEnv;
  let log = createTestLog();
  allLogLevels(log);
  let lines = await getLines();
  expect(lines.length).toEqual(5);
});

test('When explicitly set to error level, loggger displays all level 0 messages', async () => {
  process.env = testEnv;
  let log = createTestLog('error');
  allLogLevels(log);
  let lines = await getLines();
  expect(lines.length).toEqual(3);
});

test('Verbose level displays messages from level 4 and below', async () => {
  process.env = testEnv;
  let log = createTestLog('verbose');
  allLogLevels(log);
  let lines = await getLines();
  expect(lines.length).toEqual(9);
});

test('Development environment displays all messages', async () => {
  process.env.NODE_ENV = 'dev';
  let log = createTestLog();
  allLogLevels(log);
  let lines = await getLines();
  expect(lines.length).toEqual(10);
});

test('Production environment displays messages from level 2 and below', async () => {
  process.env.NODE_ENV = 'prod';
  let log = createTestLog();
  allLogLevels(log);
  let lines = await getLines();
  expect(lines.length).toEqual(5);
});

set-log-to. js:

const { createLogger, format, transports } = require('winston');
const { combine, printf, errors } = format;

const cc = require('../constants/chalk-classes');
const {
  fatal,
  caveat,
  victory,
  error,
  warn,
  attempt,
  success,
  info,
  debug,
  plain,
} = cc;

const config = {
  levels: {
    fatal: 0,
    caveat: 0,
    victory: 0,
    error: 1,
    warn: 2,
    attempt: 3,
    success: 3,
    info: 3,
    verbose: 4,
    debug: 5,
  },
};

const formatting = combine(
  errors({ stack: true }),
  printf((infoObj) => {
    const { level, message } = infoObj;
    switch (level) {
      case 'fatal':
        return `${fatal(`${level}:`)} ${plain(message)}`;

      case 'caveat':
        return `${caveat(`${level}:`)} ${plain(message)}`;

      case 'victory':
        return `${victory(`${level}:`)} ${plain(message)}`;

      case 'error':
        return `${error(`${level}: ${message}`)}`;

      case 'warn':
        return `${warn(`${level}: ${message}`)}`;

      case 'attempt':
        return `${attempt(message)}`;

      case 'success':
        return `${success(message)}`;

      case 'info':
        return `${info(message)}`;

      case 'verbose':
        return `${plain(message)}`;

      case 'debug':
        return `${debug(level)}: ${plain(message)}`;
    }
  })
);

function setLevel(level) {
  if (!level) {
    if (process.env.NODE_ENV === 'dev') {
      return (level = 'debug');
    } else {
      return (level = 'warn');
    }
  } else {
    return level;
  }
}

function setLogTo(level) {
  level = setLevel(level);
  const log = createLogger({
    levels: config.levels,
    level,
    transports: [
      new transports.Console({
        format: formatting,
      }),
    ],
  });
  return log;
}

module.exports = setLogTo;
...