1

伊夫下面的代码使用按预期工作的本地存储,我想为它创建单元测试的问题是,我在这个功能使用本地存储模拟本地存储/覆盖

这是我想测试

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 但我认为通过本地存储的参数是在这种情况下有点大材小用,因为该功能(开)从许多地方调用多次。 ..有没有更好的/更清洁的方式来处理这个问题?

+0

对此答案的建议是正确的做法。您可以像Stuart建议的那样对其进行破解,但这是一种破解,它要求您的测试对实现细节有深入的了解。如果你传递了一个可以被stubbed的对象,它就是你的API的一部分。 –

+0

@JuanMendes - 抱歉,我想念这个评论:)好吧,这是要走的路,你也认为使用这里功能的解决方案是矫枉过正? –

+0

我不确定你的意思是“认为在这里使用功能性解决方案是过度杀伤性的”我也无法理解你的问题,我只看到一种方法被使用,它似乎与函数式编程没有关系。我也不知道函数编程和覆盖有什么关系。无论如何,我可以告诉你,你发布的答案中提出的解决方案是实现它的标准方法,如果你认为你的代码必须经过测试,那么不会被视为矫枉过正。你可以走得更远,并使用依赖注入框架,但这超出了你的问题。 –

回答

2

因为你的代码被加载了各种各样的副作用,所以你在挣扎。因为您不会从this._exethat_run中返回任何值,所以在我看来,这些功能也有副作用。您最好从这些函数返回值或承诺值,而不是依赖这些函数来改变更多的外部状态。

这里是写你的模块

// 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); 
+0

非常感谢naomik 1+,但因为我不熟悉,不能在我的公司使用currenlty ES6可以请你把它改成es5,我可以测试它并使用它?谢谢先生! –

+0

我也认为这是一个很好的方法,因为它也是1+你在另一个问题中的答案。 –

+0

你能协助吗? –