Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/javascript/384.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/node.js/39.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript 节点jasmine-如何对涉及redis调用的函数进行单元测试?_Javascript_Node.js_Unit Testing_Jasmine - Fatal编程技术网

Javascript 节点jasmine-如何对涉及redis调用的函数进行单元测试?

Javascript 节点jasmine-如何对涉及redis调用的函数进行单元测试?,javascript,node.js,unit-testing,jasmine,Javascript,Node.js,Unit Testing,Jasmine,我刚开始玩Jasmine的游戏,但我还在玩间谍/模拟游戏,例如,我有一个功能 module.exports = (() => { .... function getUserInfo(id) { return new Promise((resolve, reject) => { redis.getAsync(id).then(result => { resolve(result)

我刚开始玩Jasmine的游戏,但我还在玩间谍/模拟游戏,例如,我有一个功能

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调用,使其成为真正的独立单元测试


谢谢。

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

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

下面是可能的样子

用户助手/factory.js

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

  function getUserInfo(id) {
    return redis.getAsync(id); // note simplified as new Promise was not needed
  }
  return {getUserInfo};
};
// this is the wrapper that preserves existing API
module.exports = require('./factory')(redis);
用户助手/index.js

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

  function getUserInfo(id) {
    return redis.getAsync(id); // note simplified as new Promise was not needed
  }
  return {getUserInfo};
};
// 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 () => {
    userInfo = await userHelper.getUserInfo('userid123');
  });

  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程序员,比如您的Really,以及我们的秘密咒语,如“哦,光荣的依赖注入容器,给我留下了一个
instanceof
X”:

不是OOP或DI弄错了,而是可测试性、DI、IOC等只是关于参数化

有趣的是,如果我们将redis作为一个模块加载,并且如果我们使用一个可配置的模块加载器,比如SystemJS,我们可以通过在测试级别简单地使用加载器配置来实现这一点。甚至Webpack在某种程度上也允许您这样做,但是对于NodeJ,您需要对Require函数进行猴头补丁,或者创建一堆假的软件包,这些都不是好的选择

针对OP的具体回应


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


您需要重新构造API表面,并简单地导出消费代码必须调用的工厂,而不是应用这些工厂的结果,以减轻负担,但有一些折衷办法,默认实例对消费者很有帮助。

这取决于测试用例。首先,您必须运行一个将向redis插入一些数据的测试用例。然后你必须运行这个测试case@AJS那就不是单元测试了。他想模仿redis,这是正确的方法approach@AluanHaddad系统测试从端到端覆盖整个系统。单元测试可用于描述端到端周期的一个子部分。在这种情况下,应用程序代码的单元测试将/可能不会达到目标数据库,但您可能会/可能会有涵盖数据库存储过程的单元测试。基本上,将您的应用程序按照合理的分区划分为要测试的内容。如果你选择了错误的分区线,你最终会在模拟对象和测试脚手架等方面遇到一个很大的代码维护问题。@AJ是的,对不同的东西进行不同类型的测试。模拟用于单元测试,模拟的主要用例是避免命中数据库。我同意您关于除了单个组件之外还需要测试组件之间的交互的所有观点,但我下面的回答描述了我认为在保留api的同时使此函数可测试和测试的可靠方法。谢谢!这是一个好主意,但实际上,当我有大量文件要测试时,我需要为每个文件创建一个工厂和index.js,这似乎很奇怪。这只是我偶然适应了您的具体情况。有很多其他的布局可以使用,但主要的想法只是基于IIFE结果的导出,这是一个实体模式,我使用NodeJS
/index
约定来处理它。您还可以使用一个文件,并同时导出到
exports.userHelperFactory
module.exports
。更广泛的一点是,能够模拟测试只是关于参数化,它可以是干净和简单的:)您也可以只公开工厂,并要求消费代码通常提供
redis
,这对于应用程序级组件可能很好,但如果您正在编写库,您可能希望保留您公开的当前简单API。你也可以简单地打电话给更高级别的所有工厂。这里可以做很多事情,但这种方法不需要使用第三方(或手动)DI抽象。在函数式语言中,您不需要它们,尽管它们可能很有用,而JavaScript是一种函数式语言。可测试性只是为这个非常详细的遍历添加参数!我最终使用了您提到的后一种方法,即module.exports同时导出工厂和默认帮助程序。@ggorlen谢谢。我已经相应地更新了代码。