设计模块的最佳方式,使其状态易于模拟和测试
Best way to design a module so that its state is easily mocked and tested
我有两个需求模块:
state.js
define(function () {
var stateObj = {hello:"test!"};
return {
getObj: function () {
return stateObj;
}
};
});
main.js
define(['state'],function (global) {
var privateFn = global.getObj()
return {
getHello: function () {
if(privateFn.hello == "test!")
//do someoperation
},
changeState : function() {
//changes the privateFn state
}
};
});
state
模块将对象提供给main
模块,用于内部状态存储。为main
的内部状态保留单独模块的原因是为了便于测试,因此在我的测试用例中,我可以注入一个假的state
模块,并检查main
模块的工作方式。
由于我是js世界的新手,所以我在这里采取的方法是否正确,我不确定。
如果你投了反对票,那可能是因为很难以明确的方式回答。事实上,我正在查看代码,甚至不确定你在问什么。以下是我的一些想法,但请注意,其他人可能不同意。
您的状态是单例的,所以测试起来应该足够容易。假设上面的changeState
方法如下:
changeState: function (key, value) {
privateFn[key] = value;
}
然后,您可以通过包括state
和main
来测试它,然后调用main.changeState
,看看state
是否按预期更新:
main.changeState('foo', 'bar');
expect(state.foo).toBe('bar');
其他方法(在state
不是单例的情况下(是在main
上实际公开状态。这通常是通过在它前面加下划线来完成的,这样它就被认为是私有的(这是一种命名约定,实际上并不是私有的(。然后,在你的测试中,你可以"作弊"并直接查看状态。
main.changeState('foo', 'bar');
expect(main._state.foo).toBe('bar');
另一种方式是通过依赖注入(这在棱角分明的世界里很流行(。这意味着main
需要是某种类型的工厂,或者是一个类。如果你来自java世界,那么类可能最有意义:
// Some other file where both main.js and state.js is imported
var main = new Main({
state: state
});
因此main.js
本身不导入任何内容。
我希望这能回答你的问题。
当我在打字的时候:小心你的名字。global
通常指window
(root
也是(,因此让global
指代您的状态是非常令人困惑的。将其命名为:state
。此外,您的privateFn
指的是对象,而不是函数(正如Fn
后缀所暗示的那样(。同样令人困惑。
第三件事,我不确定每个人都会同意我的观点,那就是"州"应该指的是州本身。它在这里,是一个包装。有点像BackboneJS中的Model
(其中attributes
就是我所说的state
(。区分两者很重要:
- 包装器,公开有用的方法和其他内容
- 以及实际数据(通常是一个对象(
实际数据通常可以(如果不是总是(转换为JSON并存储在服务器上发送。与Java不同(我认为?(,您的状态在任何时候都应该很容易与它的类/包装器分离。包装器可以(应该(依赖于数据,但数据绝对不应该依赖于它的包装器。
编辑
我突然意识到,也许你的意思是main.js
是数据的包装器(也是唯一的包装器(。如果是这样的话,那么我会用不同的方式来构建它。我只有一个文件。类似于:
function modelFactory (options) {
var state = options.state || {};
// Useful methods...
return {
get: function (key) {
return state[key];
},
set: function (key, value) {
state[key] = value;
// Maybe trigger some change event for others to listen for
},
toJSON: function () {
return JSON.stringify(state);
}
// ...
}
}
然后测试它,你可以做
var model = modelFactory({
state: {foo: 'bar'}
});
expect(model.get('foo')).toBe('bar');
model.set('hello', 'world');
expect(model.get('hello')).toBe('world');
我上面描述的是Backbone.Model
的基础知识。
- 事件和状态
- 获取选择框的状态
- 相位器状态未捕获参考错误
- 如何更改reactjs中外部/独立组件的状态或属性
- 如何使用密码检测网络中的状态连接
- Ember.js-接口状态应该存储在哪里
- 混合 ui-sref 和 $state.go 在 Angular ui-router 中进行状态转换
- 在Angular 2中布线期间保持零部件处于活动状态
- 在mvc应用程序中,在回发时保留最初隐藏的文本框的隐藏或可见状态
- XMLHttpRequest未返回值-状态202
- 使用javascript反复检查用户在facebook上的登录状态
- 如何使bxslider仅在移动视图中处于活动状态
- 获取ASP.NET Ajax Timer状态
- React redux初始化功能,无论状态变化如何
- 无法在现有状态转换期间更新-未使用任何非法的setState()
- 悬停下拉菜单即使在鼠标移出后也保持活动状态
- 从组件状态的数组中删除元素
- iOS 中的按钮触摸状态
- 使用AngularJS中的UI路由器将状态重定向到默认子状态
- 设计模块的最佳方式,使其状态易于模拟和测试