-
Notifications
You must be signed in to change notification settings - Fork 1.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add a test mode #962
Comments
That would require more or less to mock all the calls we do to redis, not a simple task, would be the same as having a memory backend instead of Redis. |
@entropitor Not really unit testing at that point, more like integration. Its a neat idea though and would be a cool project some people could work on. |
Check out https://github.com/yeahoffline/redis-mock - that gives you your "memory backend" that should be able to be dropped in for testing |
Also recommend: https://github.com/guilleiguaran/fakeredis |
Was anyone able to setup bull to use https://github.com/yeahoffline/redis-mock ? |
Not me. Also, seeing as bull uses https://github.com/luin/ioredis, technically it would actually be https://github.com/stipsan/ioredis-mock. Unfortunately neither redis-mock, fakeredis nor ioredis-mock are feature-complete. All throw errors for missing methods when used with Bull. In the end I have switched to using a real test redis instance and have written some proper integration tests that way. |
Leaving this here just in case.
|
@apellizzn tried your solution, got error _this.client.client is not a function near __this.client.client('setname', this.clientName()) in worker.js |
I 've been trying to use ioredis-mock as a testbed to bull I 've added lua support in a fork to it But... still I needed to add also some other commands:
Anyway I realized that a command ioredis-mock had already implemented was not behaving as expected, that's the case for: The command was not blocking until a new element enters in the right list. So I 've quit my attempt to make bull work on ioredis-mock... but if someone else wants to pick up the challenge you are welcome 👍 this is the branch where I 've added the extra commands needed by bull (and also the lua support) https://github.com/nbarrera/ioredis-mock/tree/use-as-bull-testbed |
Definitely easier to just spin up real redis in a docker container |
|
I got something (and somethings not) working earlier this year and thought I share it, with the hope that someone improves it :) // __mock__/queues.js" with Jest
import Queue from "bull";
import path from "path";
import { jobs } from "../jobs";
import Redis from "ioredis-mock";
export const redisMockClient = new Redis();
redisMockClient.info = async () => "version";
redisMockClient.client = (clientCommandName, ...args) => {
if (!redisMockClient.clientProps) {
redisMockClient.clientProps = {};
}
switch (clientCommandName) {
case "setname": {
const [name] = args;
redisMockClient.clientProps.name = name;
return "OK";
}
case "getname":
return redisMockClient.clientProps.name;
default:
throw new Error("This implementation of the client command does not support", clientCommandName);
}
};
export const jobQueue = new Queue("jobQueue", { createClient: () => redisMockClient });
jobQueue.process(path.join(__dirname, "..", "processor.js"));
const jobNames = Object.keys(jobs);
jobNames.forEach((jobName) => {
jobQueue.process(jobName, 5, path.join(__dirname, "..", "processor.js"));
}); I was brought up to believe that you should decouple your tests. Spinning up a real redis instance or mongodb or whatever feels wrong. I used mongodb-memory-server for mongodb successfully for tests. I am surprised it was not as easy with Bull and Redis |
Is there any progress with this? |
I got something working using ioredis-mock. I am using Jest and here is my mock set-up for my tests: jest.mock('ioredis', () => {
const Redis = require('ioredis-mock');
if (typeof Redis === "object") {
// the first mock is an ioredis shim because ioredis-mock depends on it
// https://github.com/stipsan/ioredis-mock/blob/master/src/index.js#L101-L111
Redis.Command = { _transformer: { argument: {}, reply: {} } };
return {
Command: { _transformer: { argument: {}, reply: {} } }
}
}
// second mock for our code
return function (...args) {
var instance = new Redis(args);
instance.options = {};
instance.info = async () => "version";
instance.client = (clientCommandName, ...args) => {
if (!instance.clientProps) {
instance.clientProps = {};
}
switch (clientCommandName) {
case "setname": {
const [name] = args;
instance.clientProps.name = name;
return "OK";
}
case "getname":
return instance.clientProps.name;
default:
throw new Error("This implementation of the client command does not support", clientCommandName);
}
};
return instance;
}
}) This is based on a code from this thread and a thread about using ioredis-mock with Jest. The issue I am facing is that ioredis-mock has Lua implementation version 5.3, where Redis uses 5.1 and this breaks some scripts. For example it is not possible to add delayed task because it uses bit.band() which is not available on Lua 5.3 as it supports bit-wise operations by internally. |
I've tried your set up, but have run into an issue where my processor is not picking up any of the jobs that are posted. I can see when I post a job that bull adds keys to the redis mock. Have you got simple job posting and processing working with the mock? Here's a simplified version of my test: const wait = async function wait(time) {
return new Promise(resolve => setTimeout(resolve, time));
};
jest.mock('ioredis', () => {
// ioredis-mock v4.18.2
const Redis = require('ioredis-mock');
if (typeof Redis === "object") {
// the first mock is an ioredis shim because ioredis-mock depends on it
// https://github.com/stipsan/ioredis-mock/blob/master/src/index.js#L101-L111
Redis.Command = { _transformer: { argument: {}, reply: {} } };
return {
Command: { _transformer: { argument: {}, reply: {} } }
}
}
// second mock for our code
return function (...args) {
var instance = new Redis(args);
instance.options = {};
instance.info = async () => "version";
instance.client = (clientCommandName, ...args) => {
if (!instance.clientProps) {
instance.clientProps = {};
}
switch (clientCommandName) {
case "setname": {
const [name] = args;
instance.clientProps.name = name;
return "OK";
}
case "getname":
return instance.clientProps.name;
default:
throw new Error("This implementation of the client command does not support", clientCommandName);
}
};
return instance;
}
});
// bull v3.12.0
const Queue = require('bull');
// ioredis v4.14.0
const Redis = require('ioredis');
describe('test', () => {
it('test', async () => {
expect.assertions(2);
let redis = new Redis();
const queue = new Queue('test', {
createClient: () => {
return redis;
}
});
const theJob = { 'status': true };
queue.process(async (job, done) => {
// does not get this far
console.log('processing: ', job.data);
expect(job.data).toMatchObject(theJob);
done(null, job.data);
});
await queue.add(theJob);
let keyRes = await redis.keys('*');
// Prints [ 'bull:test:id', 'bull:test:1.0', 'bull:test:wait' ]
console.log(keyRes);
await wait(100);
expect(1).toBe(1);
});
}); |
For anyone that stumbles upon this issue - neither Bull 3, nor the upcoming Bull 4 currently natively support a test/in-memory environment for local development, and no mock clients (ioredis-mock, redis-mock) work with Bull. I've managed to find a working solution using redis-server const startLocalRedisServer = async () => {
const MOCK_SERVER_PORT = 6379;
try {
const RedisServer = require('redis-server');
const server = new RedisServer();
await server.open({ port: MOCK_SERVER_PORT });
} catch (e) {
console.error('unable to start local redis-server', e);
process.exit(1);
}
return MOCK_SERVER_PORT;
};
// Bull setup
const setupQueue = async () => {
const port = await startLocalRedisServer();
return new Queue('General', { redis: { port } });
} |
Probably this issue in ioredis-mock (apart from the Lua version) has something to do with the fact that bull is not working properly with it: stipsan/ioredis-mock#773 |
This approach works perfectly for me: In my
and in my bullmq instance setup file, i included
|
I tried this and Bull still tries to connect to the default Redis instance in my tests. I'm using Edit: OK, I made it work by adding the next lines:
Apparently this is what BullMQ 4.0 checks to see if it can reuse the connection: https://github.com/taskforcesh/bullmq/blob/36726bfb01430af8ec606f36423fc187e4a06fb4/src/utils.ts#L41 |
I don't know if it's useful but Shoryuken which is used for SQS queues in Ruby has something called an inline adapter used to run jobs inline. So, instead of the task getting added to a queue, it just runs the perform function. Maybe this is a good idea to do with bull? Here is a reference: https://github.com/phstc/shoryuken/wiki/Shoryuken-Inline-adapter |
@tom-james-watson Hello. You mentioned that you use a real Redis instance for your integration tests. I'm looking to do the same, and I was hoping I could ask you a question about your implementation. Suppose you just have a simple POST endpoint that enqueues a job with Bull. Suppose also that you use something like Supertest to make a POST Request to that endpoint. When the POST Request returns 201 or 202, you can't immediately start asserting that side-effects of the processed jobs have occurred correctly because there could be a small delay between the POST response and the actual execution of the job. I was wondering how you handle that with your setup. Thanks. |
The short answer is to listen to the queue's |
@tom-james-watson Thanks very much, so I take it, with Jest for example, you'd use something like In the event that you have deffered jobs (say 15 minutes in the future), would you just try to get away with asserting that the job was added with the correct delay and not worry about its execution? Thanks again. |
Let's not derail this thread - every time you comment you're sending notifications to everyone who has participated in this thread. But yes, most of my tests simply check check that jobs are added with the expected parameters. For these I mock the |
Thanks. I agree we're sightly off topic from the original objective of the thread, but I figured the information was somewhat relevant for those performing integration tests with Bull. Anyway, I'll leave it at that and thanks for your time. |
Trying to use @prithvin suggested solition using
|
Someone that got this to work with bull v3? For some reason when i try @prithvin solution my test suite gets stuck (without any errors getting thrown). |
@stigvanbrabant I didn’t and ended up switching to bullmq and just starting a Docker container with redis before running the test suite. |
I'm not doing anything sophisticated with bull, so I created this mock that executes the job the moment a job is added, maybe someone can expand on this. // __mocks__/bull.ts
export default class Queue {
name: string
constructor(name) {
this.name = name
}
process = (fn) => {
console.log(`Registered function ${this.name}`)
this.processFn = fn
}
add = (data) => {
console.log(`Running ${this.name}`)
return this.processFn({ data })
}
} |
My use case is not to execute jobs scheduled during testing. I was hoping to solve this by instantiating Bull using different default job options, such as How about supporting I'm using NestJS and also posted a question on SO: https://stackoverflow.com/questions/67489690/nestjs-bull-queues-how-to-skip-processing-in-test-env/67489691 |
DISCLAIMER: this applies to bullmq (which you should use), not bulljs (v3). So we would just need to mock that one, I think. This currently works for me just fine: import * as bullUtils from 'bullmq/dist/utils';
jest.mock('ioredis', () => require('ioredis-mock/jest'));
const bullMock = jest.spyOn(bullUtils, 'isRedisInstance');
bullMock.mockImplementation((_obj: unknown) => {
return true;
}); If |
prithvin solution almost works though there were some extra pain points if you try to pause the queues in a unit test. This is for Bull 4.0 which does some extra disconnect / connect things.
Then before your jest tests
|
Thankyou so much for this, do you have any examples how your testing your workers queues? Just all new to me and could use some pointers on meaningful unit tests. |
I ended up using https://github.com/mhassan1/redis-memory-server and worked great |
Closing this as no more actions from our side and it seems there are solutions available. |
It would be nice if bull had a test mode, just like kue: https://github.com/Automattic/kue#testing
This way it's much easier to write unit tests against bull workers
The text was updated successfully, but these errors were encountered: