Я хочу провести модульное тестирование своего 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};
Итак, теперь я нахожусь в убыток; смущает и сбивает с толку насмешки, заглушки и внедрение зависимостей (как здесь, так и в других тестах). Что я делаю не так? Как лучше всего провести модульное тестирование такой функциональности?