设计模块的最佳方式,使其状态易于模拟和测试

Best way to design a module so that its state is easily mocked and tested

本文关键字:状态 易于 模拟 测试 模块 最佳 方式      更新时间:2023-09-26

我有两个需求模块:

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;
}

然后,您可以通过包括statemain来测试它,然后调用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(。区分两者很重要:

  1. 包装器,公开有用的方法和其他内容
  2. 以及实际数据(通常是一个对象(

实际数据通常可以(如果不是总是(转换为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的基础知识。