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

你能協助嗎? –