代码之家  ›  专栏  ›  技术社区  ›  Ben Aston

用于实例化单个对象的模式,同时保持可测试性

  •  0
  • Ben Aston  · 技术社区  · 7 年前

    当我的应用程序启动时,它实例化Redis客户端对象:

    const redisClient = redis.createClient(REDIS_URL);
    

    我维护一个代理对象 myRedisClient 它公开了客户端对象公开的API的预期版本:

    const get = (key) => 
        new Promise((resolve, reject) =>
            redisClient.get(key, (err, resp) => 
                err ? reject(err) : resolve(resp))
    
    const myRedisClient = { get };
    

    简单地拥有是不对的 redisClient 隐式实例化,因此我想将其放在一个要显式调用的方法中:

    let redisClient = null;
    const init = () => redisClient || redis.createClient(REDIS_URL);
    const myRedisClient = { init, get};
    

    get 再贴现客户 . 如果 收到 再贴现客户 那么这很简单。

    如果 收到 但是,在另一个文件中, 可以通过关闭并添加 再贴现客户 作为 收到

    // my-redis-client.js
    import get from './get';
    
    let redisClient = null;
    const init = () => redisClient || redis.createClient(REDIS_URL);
    const myRedisClient = { init, get(key) => get(redisClient, key) };
    
    // get.js
    const get = (redisClient, key) => 
        new Promise((resolve, reject) =>
            redisClient.get(key, (err, resp) => 
                err ? reject(err) : resolve(resp));
    

    最后,我希望能够把它删掉 再贴现客户 ,我想我可以退出 redis.createClient

    1 回复  |  直到 7 年前
        1
  •  1
  •   Bergi    7 年前

    简单地拥有是不对的 redisClient

    不,这没什么错,这正是你的应用在启动时应该做的。

    你怎么了 再贴现客户 它是一个有状态的全局变量,并且 myRedisClient 隐式依赖 在上面。将 在一个方法中,显式调用并不能解决这两个问题。

    再贴现客户 任何一个

    • global.redisClient = … 启动时。对于模拟,您可以轻松地覆盖全局(如果您不想使用独立模拟并行运行多个测试)。
    • 明确依赖关系和
      • 使用模块系统,其中 myRedisClient.js 取决于 redisClient.js 导出“全局”静态值,但让您的测试系统使用带有依赖项注入的模块加载程序的模块
      • 为您的应用程序创建构造函数或工厂函数 这样您就可以通过显式地传递 再贴现客户 去吧。

    最后一个解决方案似乎符合你试图做的事情的精神。应该是这样的:

    // my-redis-client.js
    default export function init(redisClient) {
        const get = util.promisify(redisClient.get).bind(redisClient);
    
        return { get };
    }
    // main.js
    import initMyClient from 'my-redis-client'
    const myRedisClient = initMyClient(redis.createClient(REDIS_URL));