Как я могу издеваться над Redis при модульном тестировании приложения Express node.js? - PullRequest
0 голосов
/ 30 мая 2020

Я хочу провести модульное тестирование своего express приложения, части которого устанавливают и получают данные из базы данных Redis. Исследуя, почему некоторые из моих тестов периодически выходят из строя - и все чаще по мере того, как я продолжаю разрабатывать свое приложение, я понял, что мои тесты по-прежнему вызывают сервер Redis, а не тот, который, как я думал, я высмеял в памяти.

Как можно Мне удалось создать имитацию клиента Redis (ioredis), чтобы я мог выполнять модульное тестирование частей моего кода изолированно друг от друга и без вызова сервера Redis?

Вот что у меня есть на данный момент. 1006 * Тестируемый код

У меня есть модель приложения, которая объединяет значения из пакета. json со значениями из моей базы данных Redis, часто с резервными значениями. Значения можно добавлять или получать.

// models/application.js

import fs from 'fs';
import {client} from '../config/database.js';

export const getAll = async () => {
  const data = await client.hgetall('application');
  const package_ = JSON.parse(fs.readFileSync('package.json'));

  const application = {
    name: data.name || 'IndieKit',
    version: package_.version,
    description: package_.description,
    repository: package_.repository,
    locale: data.locale || 'en',
    themeColor: data.themeColor || '#0000ee'
  };

  return application;
};

export const get = async key => {
  const application = await getAll();
  return application[key];
};

export const setAll = async values => {
  return client.hmset('application', values);
};

export const set = async (key, value) => {
  return client.hset('application', key, value);
};

client вызывается из отдельного файла, где я подключаюсь к серверу Redis, используя ioredis :

// config/database.js

import Redis from 'ioredis';

export const client = new Redis(process.env.REDIS_URL);
export const expires = 3600;

client.on('error', error => {
  console.error('Redis', error);
});

Тест

Я использую Ava в качестве своей среды тестирования, с rewiremock , используемым для замены зависимости ioredis на ioredis-mock … или около того Я подумал!

// tests/models/application.js

import test from 'ava';
import {rewiremock} from '../helpers/rewiremock.js';
import {client} from '../../config/database.js';

test.beforeEach(async t => {
  t.context.applicationModel = await rewiremock.proxy(() => {
    return import('../../models/application.js');
  });
});

test.afterEach.always(() => {
  client.flushall();
});

test.serial('Gets a value', async t => {
  await t.context.applicationModel.set('name', 'foobar1');
  const result = await t.context.applicationModel.get('name');
  t.is(result, 'foobar1');
});

test.serial('Gets all values', async t => {
  await t.context.applicationModel.set('name', 'foobar2');
  const result = await t.context.applicationModel.getAll();
  t.is(result.name, 'foobar2');
});

test.serial('Sets a value', async t => {
  await t.context.applicationModel.set('name', 'foobar3');
  const result = await t.context.applicationModel.get('name');
  t.is(result, 'foobar3');
});

test.serial('Sets all values', async t => {
  await t.context.applicationModel.setAll({
    name: 'foobar4',
    locale: 'bazqux1'
  });
  const result = await t.context.applicationModel.getAll();
  t.is(result.name, 'foobar4');
  t.is(result.locale, 'bazqux1');
});

Глядя на это снова, очевидно, есть пара проблем. Сначала я выполнял команду flushAll на client, которая взаимодействовала напрямую с сервером Redis, а не с имитацией в памяти. Во-вторых, необходимость использовать разные значения для каждого теста должна была предупредить меня о том, что что-то не так; Мне бы это не понадобилось, если бы я действительно очищал базу данных после каждого теста.

Вот помощник rewiremock, который я импортирую в этот тест:

import rewiremock from 'rewiremock/node.js';

rewiremock('ioredis')
  .by('ioredis-mock');

export {rewiremock};

Итак, теперь я нахожусь в убыток; смущает и сбивает с толку насмешки, заглушки и внедрение зависимостей (как здесь, так и в других тестах). Что я делаю не так? Как лучше всего провести модульное тестирование такой функциональности?

...