当前位置:  开发笔记 > 编程语言 > 正文

节点茉莉花-如何对涉及Redis调用的功能进行单元测试?

如何解决《节点茉莉花-如何对涉及Redis调用的功能进行单元测试?》经验,为你挑选了1个好方法。

我刚开始在茉莉花周围玩耍,但我仍在努力探索/模拟东西,例如,我有一个功能

module.exports = (() => {
    ....

    function getUserInfo(id) {
        return new Promise((resolve, reject) => {
            redis.getAsync(id).then(result => {
                resolve(result)
            })
        }) 
    }
    return { getUserInfo: getUserInfo }
})()

然后我开始编写茉莉花规格

describe('Test user helper', () => {
    let userInfo

    beforeEach(done => {
        userHelper.getUserInfo('userid123')
            .then(info => {
                userInfo = info
                done() 
            })
    })

    it('return user info if user is found', () => {
        expect(userInfo).toEqual('info of userid 123')
    })
})

它运行良好,但是我的问题是如何模拟redis.getAsync调用,这样它才能成为真正的隔离单元测试?

谢谢。



1> Aluan Haddad..:

好问题。您可以模拟redis依赖关系,但前提是您稍微重写一下代码以使其更具可测试性。在这里,这意味着将redis设为返回包含的对象的工厂的参数getUserInfo

当然,这会更改API,调用者现在需要调用export来获取对象。为了解决这个问题,我们可以创建一个包装器模块,该包装器模块使用标准redis对象调用该函数,然后返回结果。然后,我们将实际工厂移至内部模块,该模块仍可以对其进行测试。

这可能是什么样子

user-helper / factory.js

module.exports = redis => {
  ....

  function getUserInfo(id) {
    return redis.getAsync(id); // note simplified as new Promise was not needed
  }
  return {getUserInfo};
};

user-helper / index.js

// this is the wrapper that preserves existing API
module.exports = require('./factory')(redis);

现在进行测试

const userHelperFactory = require('./user-helper/factory');

function createMockRedis() {
  const users = [
    {userId: 'userid123'},
    // etc.
  ];
  return {
    getAsync: function (id) {
      // Note: I do not know off hand what redis returns, or if it throws,
      // if there is no matching record - adjust this to match.
      return Promise.resolve(users.find(user => user.userId === id));
    }
  };
}

describe('Test user helper', () => {
  const mockRedis = createMockRedis();
  const userHelper = userHelperFactory(mockRedis);

  let userInfo;

  beforeEach(async done => {
    userInfo = await userHelper.getUserInfo('userid123');
    done();
  });

  it('must return user info when a matching user exists', () => {
    expect(userInfo).toEqual('info of userid 123');
  });
});

注意:正如评论中所讨论的,这只是我对当前情况的偶然处理。您可以使用许多其他设置和约定,但是主要思想只是基于IIFE结果的现有导出,这是一个可靠的模式,我利用NodeJS /index约定来保留现有API。你也可以使用通过双方一个文件,出口module.exports = factory(redis)module.exports.factory = factory,但这样会,我相信,在较少的NodeJS地道。更广泛的观点是,能够模拟测试,而可测性通常只是参数化。

参数化功能非常强大,其简单性就是为什么使用函数式语言工作的开发人员有时会嘲笑OOP程序员(例如您的确如此),以及我们的秘密咒语,例如“哦光荣的Dependency Injection Container,给我一个instanceofX” :)

不是说OOP或DI搞错了,而是因为可测试性,DI,IOC等只是关于参数化。

有趣的是,如果我们将redis作为模块进行加载,并且如果使用的是可配置的模块加载器(例如SystemJS),则可以通过在测试级别上简单地使用加载器配置来实现。甚至Webpack都可以在某种程度上允许您执行此操作,但是对于NodeJS,您将需要猴子修补Require Function或创建一堆伪造的软件包,这不是很好的选择。

对OP的具体回应

谢谢!这是个好主意,但实际上,当我要测试大量文件时,我需要为每个文件创建一个工厂和index.js,这似乎很奇怪。

您将需要重新构建API表面,并简单地导出消耗代码必须调用的工厂(而不是应用这些工厂的结果)来减轻负担,但是需要权衡取舍,默认实例对消费者很有帮助。

推荐阅读
郑小蒜9299_941611_G
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有