我刚刚开始玩茉莉花,我仍在挣扎着狡猾/嘲弄的事情,例如,我有一个功能
import QtQuick.Window 2.2
import QtQuick.Controls 2.1
Popup {
x: 100
y: 100
width: 200
height: 300
modal: true
focus: true
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutsideParent
visible: false
}
然后我开始编写Jasmine规范
module.exports = (() => {
....
function getUserInfo(id) {
return new Promise((resolve, reject) => {
redis.getAsync(id).then(result => {
resolve(result)
})
})
}
return { getUserInfo: getUserInfo }
})()
它运行良好,但我的问题是如何模拟redis.getAsync调用,以便它可以成为一个真正的隔离单元测试?
感谢。
答案 0 :(得分:4)
好问题。你可以模拟出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};
};
用户辅助/ 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程序员,比如你的真实,以及我们的秘密咒语,如“哦光荣的依赖注入容器,遗留给我instanceof
X“:)
不是OOP或DI弄错了可测性,DI,IOC等只是参数化。
有趣的是,如果我们将redis作为模块加载,并且如果我们使用可配置的模块加载器,例如SystemJS,我们可以通过在测试级别使用加载器配置来实现。甚至Webpack也可以让你在某种程度上做到这一点,但是对于NodeJS,你需要修补需求函数,或者创建一堆伪包,这不是很好的选择。
OP的具体回复
谢谢!这是一个好主意,但实际上,当我需要测试大量文件时,我需要为每个文件创建一个工厂和index.js,这似乎很奇怪。
您需要重新构建API表面并简单地导出消耗代码必须调用的工厂,而不是应用这些工厂的结果,以减轻负担,但是存在权衡和默认实例对消费者有帮助。