模拟本地存储以测试两种功能/覆盖方法

时间:2016-11-07 12:41:20

标签: javascript node.js unit-testing functional-programming sinon

以下代码使用了按预期工作的本地存储,我想为它创建单元测试问题是我在此函数中使用本地存储

这是我要测试的功能

open: function(url) {
            var that = this;
            if (!localStorage.getItem(“alert”)) {               
                that_run(url)
                    .then(function(isBlocked) {
                        if (isBlocked) { 
                            that._notify();
                            localStorage.setItem(“alert”, true);
                        } else { 
                            localStorage.setItem(“alert”, true);
                            that._exe(“page”,url)
                        }
                    }).done();
            } else {
                that._exe(“page”,url)
            }
        },

这是正在进行的测试,但我认为重写窗口是不好的做法,我的问题是否可以更好地编写此测试?

it.only("test for open", function () {
    var url = "http://mytesturl”;
    winlocalStorage.getItem = function(){
        return false;
    };
    var oSimulateSpy = sandbox.spy(exc "_simulate");
    return orun.open(url).then(function(){
        expect(oSimulateSpy.called).to.be.true;
    });
}); 

我看过这篇文章,并使用了功能编程 https://stackoverflow.com/a/20153543/6124024 但是我认为在这种情况下传递本地存储作为参数有点矫枉过正,因为这个函数(open)在很多地方被多次调用...是否有更好/更清晰的方法来处理它?<​​/ p>

1 个答案:

答案 0 :(得分:1)

你的挣扎是因为你的代码加载了各种各样的副作用。因为您没有从this._exethat_run返回任何值,所以对我来说很明显,这些函数也存在副作用。你最好使用这些函数的返回值或Promised值,而不是依赖这些函数来改变更多的外部状态。

这是编写模块的更好方法

// use "wrapper" function that configures this module
export default function(storage) {
  return {
    // other functions ...
    async open (url) {
      if (storage.getItem('alert')) {
        return this._exe('page', url) // make sure _exe returns promise
      }
      else if (await that_run(url)) {
        storage.setItem('alert', true)
        return this.notify() // make sure notify returns Promise
        // or return Promise.resolve()
      }
      else {
        storage.setItem('alert', true)
        return this._exe('page', url)
    }
  }
}

在测试代码中使用您的模块

// import mock storage adapter
const MockStorage = require('./mock-storage');

// create mock storage adapter
const mockStorage = new MockStorage();

// pass mock storage adapter to your module as a config argument
const myModule = require('./my-module')(mockStorage);

// don't forget to reset storage before each test
beforeEach(function() {
  mockStorage.reset();
});

it("test for open", async function () {
  var url = "http://mytesturl";
  mockStorage.set('alert', false);
  let someValue = await myModule.open(url);
  assert.equal(someValue, someExpectation);
  assert.equal(mockStorage.get('alert'), true);
});

模拟存储适配器可能如下所示

export default class MockStorage {
  constructor () {
    this.storage = new Map();
  }
  setItem (key, value) {
    this.storage.set(key, value);
  }
  getItem (key) {
    return this.storage.get(key);
  }
  removeItem (key) {
    this.storage.delete(key);
  }
  clear () {
    this.constructor();
  }
}

然后,当您在生产代码中使用模块时,您可以传递真实的localStorage

// use window.localStorage as config argument in production code
const myModule = require('./my-module')(window.localStorage);