Open greatghoul opened 10 months ago
eggjs 这个框架对测试的支持还算不错,不过最近在使用 egg-redis 这个插件时,遇到了一些测试上面的问题。
为避免测试互相影响,我会在每个用例后清一次 redis
# test/.setup.js afterEach(async function () { await app.redis.flushall() })
这样虽然满足了测试的要求,但会有一些副作用。
其实我们可以使用 redis mock 来解决这两个副作用,egg-redis 这个插件使用的是 ioredis 这个包,我们可以在测试环境的配置文件中覆盖 egg-redis 使用的 ioredis 版本(需要 egg-redis 2.1.0 以上的版本)。
ioredis 有一个还算成熟的 mock 实现 ioredis-mock,我们可以使用这个库。
// config/config.unittest.js config.redis = { Redis: require('ioredis-mock'), client: { data: {} } }
这样测试环境下,redis 就不会去连接真实的 redis 服务了,而是在内存中模拟,速度上就很快了,也不会对开发环境的数据造成影响。
不过在具体测试时,发现了测试无法执行,因为 redis 连接超时。
2019-05-27 23:46:46,441 WARN 37722 [egg:core:ready_timeout] 10 seconds later /path/to/project/node_modules/egg-redis/lib/redis.js:53:7 was still unable to finish.
阅读一下这部分的代码,我们可以发现,应用一直没有收到 redis ready 事件,redis 插件无法初始化。
# https://github.com/eggjs/egg-redis/blob/master/lib/redis.js#L53-L57 app.beforeStart(async () => { const index = count++; await awaitFirst(client, [ 'ready', 'error' ]); // <------ app.coreLogger.info(`[egg-redis] instance[${index}] status OK, client ready`); });
我们再去看看 redis-mock 的实现,会发现,它的 Redis 实例在初始化的方法里面,要么立即就推送了 ready 事件,要么压根不推,这与实际情况的 redis 连接是不相符的。
egg-redis 傻傻的在等待一个永远收不到的事件,所以测试就卡壳了。
# https://github.com/stipsan/ioredis-mock/blob/master/src/index.js#L44-L47 class RedisMock extends EventEmitter { constructor(options = {}) { // ... if (optionsWithDefault.lazyConnect === false) { this.connected = true; emitConnectEvent(this); } } // ... }
等待作者去修改是比较慢的,我们就先自己黑科技解决一下吧,用一个自定义的 Redis Class 继承自 ioredis-mock 的 Redis Class,在初始化的方法中延迟推送一个 ready 事件。这样去跑测试的时候,就可以顺利的走下去了。
// config/config.unittest.js const Redis = require('ioredis-mock') class RedisMock extends Redis { constructor (options = {}) { super(options) setTimeout(() => { this.emit('connect') this.emit('ready') }, 1000) } } module.exports = appInfo => { const exports = {} exports.redis = { Redis: RedisMock, client: { data: {} } } return exports }
slug: eggjs-redis-mock date: 2019-05-28
eggjs 这个框架对测试的支持还算不错,不过最近在使用 egg-redis 这个插件时,遇到了一些测试上面的问题。
为避免测试互相影响,我会在每个用例后清一次 redis
这样虽然满足了测试的要求,但会有一些副作用。
其实我们可以使用 redis mock 来解决这两个副作用,egg-redis 这个插件使用的是 ioredis 这个包,我们可以在测试环境的配置文件中覆盖 egg-redis 使用的 ioredis 版本(需要 egg-redis 2.1.0 以上的版本)。
ioredis 有一个还算成熟的 mock 实现 ioredis-mock,我们可以使用这个库。
这样测试环境下,redis 就不会去连接真实的 redis 服务了,而是在内存中模拟,速度上就很快了,也不会对开发环境的数据造成影响。
不过在具体测试时,发现了测试无法执行,因为 redis 连接超时。
2019-05-27 23:46:46,441 WARN 37722 [egg:core:ready_timeout] 10 seconds later /path/to/project/node_modules/egg-redis/lib/redis.js:53:7 was still unable to finish.
阅读一下这部分的代码,我们可以发现,应用一直没有收到 redis ready 事件,redis 插件无法初始化。
我们再去看看 redis-mock 的实现,会发现,它的 Redis 实例在初始化的方法里面,要么立即就推送了 ready 事件,要么压根不推,这与实际情况的 redis 连接是不相符的。
egg-redis 傻傻的在等待一个永远收不到的事件,所以测试就卡壳了。
等待作者去修改是比较慢的,我们就先自己黑科技解决一下吧,用一个自定义的 Redis Class 继承自 ioredis-mock 的 Redis Class,在初始化的方法中延迟推送一个 ready 事件。这样去跑测试的时候,就可以顺利的走下去了。