Wie man localStorage in JavaScript-Einheitentests verspottet?

Lesezeit: 6 Minuten

Benutzer-Avatar
Anthony Sotile

Gibt es irgendwelche Bibliotheken da draußen zu verspotten localStorage?

Ich habe verwendet Sinon.JS für die meisten habe ich andere javascript verspottet und fand es echt toll.

Meine ersten Tests zeigen, dass localStorage sich weigert, in Firefox (sadface) zuweisbar zu sein, also brauche ich wahrscheinlich eine Art Hack dafür:/

Meine Optionen ab jetzt (wie ich sehe) sind wie folgt:

  1. Erstellen Sie Wrapping-Funktionen, die mein gesamter Code verwendet, und verspotten Sie diese
  2. Erstellen Sie eine Art (möglicherweise komplizierte) Zustandsverwaltung (Snapshot von localStorage vor dem Test, bei der Bereinigung, Wiederherstellungs-Snapshot) für localStorage.
  3. ??????

Was halten Sie von diesen Ansätzen und glauben Sie, dass es andere bessere Möglichkeiten gibt, dies zu erreichen? In jedem Fall werde ich die resultierende “Bibliothek”, die ich am Ende erstelle, auf Github für Open-Source-Güte stellen.

  • Du hast #4 verpasst: Profit!

    – Chris Laplante

    14. Juli 2012 um 16:27 Uhr

Benutzer-Avatar
Andreas Köberle

Hier ist eine einfache Möglichkeit, es mit Jasmine zu verspotten:

let localStore;

beforeEach(() => {
  localStore = {};

  spyOn(window.localStorage, 'getItem').and.callFake((key) =>
    key in localStore ? localStore[key] : null
  );
  spyOn(window.localStorage, 'setItem').and.callFake(
    (key, value) => (localStore[key] = value + '')
  );
  spyOn(window.localStorage, 'clear').and.callFake(() => (localStore = {}));
});

Wenn Sie den lokalen Speicher in all Ihren Tests verspotten möchten, deklarieren Sie die beforeEach() oben gezeigte Funktion im globalen Umfang Ihrer Tests (der übliche Ort ist a specHelper.js Skript).

  • +1 – Sie könnten dies auch mit Sinon tun. Der Schlüssel ist, warum Sie sich die Mühe machen, das gesamte localStorage-Objekt zu verspotten, verspotten Sie einfach die Methoden (getItem und/oder setItem), an denen Sie interessiert sind.

    – s1mm0t

    5. April 2013 um 7:38 Uhr

  • Achtung: Es scheint ein Problem mit dieser Lösung in Firefox zu geben: github.com/pivotal/jasmine/issues/299

    – cthulhu

    17. Oktober 2013 um 13:28 Uhr

  • Ich bekomme ein ReferenceError: localStorage is not defined (laufende Tests mit FB Jest und npm) … irgendwelche Ideen, wie man das umgehen kann?

    – FeifanZ

    31. Mai 2014 um 3:03 Uhr

  • Versuchen Sie auszuspionieren window.localStorage

    – Benj

    8. Februar 2015 um 22:12 Uhr

  • andCallFake gewechselt zu and.callFake in Jasmin 2.+

    – Venugopal

    4. Januar 2016 um 10:49 Uhr

Benutzer-Avatar
a8m

verspotten Sie einfach den globalen localStorage / sessionStorage (sie haben die gleiche API) für Ihre Bedürfnisse.
Zum Beispiel:

 // Storage Mock
  function storageMock() {
    let storage = {};

    return {
      setItem: function(key, value) {
        storage[key] = value || '';
      },
      getItem: function(key) {
        return key in storage ? storage[key] : null;
      },
      removeItem: function(key) {
        delete storage[key];
      },
      get length() {
        return Object.keys(storage).length;
      },
      key: function(i) {
        const keys = Object.keys(storage);
        return keys[i] || null;
      }
    };
  }

Und was Sie dann tatsächlich tun, ist ungefähr so:

// mock the localStorage
window.localStorage = storageMock();
// mock the sessionStorage
window.sessionStorage = storageMock();

  • Ab 2016 scheint dies in modernen Browsern nicht zu funktionieren (geprüft Chrome und Firefox); überschreiben localStorage insgesamt nicht möglich.

    – jakub.g

    13. Januar 2016 um 17:41 Uhr


  • Ja, das geht leider nicht mehr, aber das würde ich auch behaupten storage[key] || null ist falsch. Wenn storage[key] === 0 es wird zurückkehren null stattdessen. Ich denke, du könntest es tun return key in storage ? storage[key] : null obwohl.

    – redbmk

    21. Dezember 2016 um 15:10 Uhr

  • Habe das gerade bei SO! Funktioniert wie ein Zauber – Sie müssen nur localStor auf localStorage zurückändern, wenn Sie sich auf einem echten Server befinden function storageMock() { var storage = {}; return { setItem: function(key, value) { storage[key] = value || ''; }, getItem: function(key) { return key in storage ? storage[key] : null; }, removeItem: function(key) { delete storage[key]; }, get length() { return Object.keys(storage).length; }, key: function(i) { var keys = Object.keys(storage); return keys[i] || null; } }; } window.localStor = storageMock();

    – mplungjan

    18. Januar 2019 um 10:17 Uhr

  • @a8m Ich erhalte einen Fehler, nachdem ich den Knoten auf 10.15.1 aktualisiert habe TypeError: Cannot set property localStorage of #<Window> which has only a gettereine Idee wie ich das beheben kann ?

    – Tasawer Nawaz

    10. April 2019 um 7:15 Uhr

  • Um setItemes sollte sein storage[key] = ${value}“ statt storage[key] = value || '' weil du es kannst sessionStorage.setItem('foo', undefined) und es wird undefiniert (oder null) als Zeichenfolge gespeichert.

    – Tanguy_k

    4. März 2020 um 13:16 Uhr

Die aktuellen Lösungen funktionieren nicht in Firefox. Dies liegt daran, dass localStorage von der HTML-Spezifikation als nicht änderbar definiert ist. Sie können dies jedoch umgehen, indem Sie direkt auf den Prototyp von localStorage zugreifen.

Die browserübergreifende Lösung besteht darin, die Objekte zu verspotten Storage.prototype z.B

Anstatt von spyOn(localStorage, ‘setItem’) verwenden

spyOn(Storage.prototype, 'setItem')
spyOn(Storage.prototype, 'getItem')

genommen von bzbarsky und teogeo‘s Antworten hier https://github.com/jasmine/jasmine/issues/299

  • Dein Kommentar sollte mehr Likes bekommen. Vielen Dank!

    – Loris Bachert

    10. September 2019 um 14:05 Uhr

  • Einverstanden, dies ist die beste Antwort, um das Problem auch mit Prebid-PRs anzugehen!

    – Jason Lydon

    9. Dezember 2021 um 19:44 Uhr

Benutzer-Avatar
Claudijo

Ziehen Sie auch die Option in Betracht, Abhängigkeiten in die Konstruktorfunktion eines Objekts einzufügen.

var SomeObject(storage) {
  this.storge = storage || window.localStorage;
  // ...
}

SomeObject.prototype.doSomeStorageRelatedStuff = function() {
  var myValue = this.storage.getItem('myKey');
  // ...
}

// In src
var myObj = new SomeObject();

// In test
var myObj = new SomeObject(mockStorage)

Im Einklang mit Mocking und Unit-Tests vermeide ich gerne das Testen der Speicherimplementierung. Zum Beispiel macht es keinen Sinn zu prüfen, ob sich die Speicherdauer erhöht hat, nachdem Sie ein Element eingestellt haben usw.

Da es offensichtlich unzuverlässig ist, Methoden auf dem echten localStorage-Objekt zu ersetzen, verwenden Sie einen “dummen” mockStorage und stubben Sie die einzelnen Methoden wie gewünscht, wie zum Beispiel:

var mockStorage = {
  setItem: function() {},
  removeItem: function() {},
  key: function() {},
  getItem: function() {},
  removeItem: function() {},
  length: 0
};

// Then in test that needs to know if and how setItem was called
sinon.stub(mockStorage, 'setItem');
var myObj = new SomeObject(mockStorage);

myObj.doSomeStorageRelatedStuff();
expect(mockStorage.setItem).toHaveBeenCalledWith('myKey');

Das ist was ich mache…

var mock = (function() {
  var store = {};
  return {
    getItem: function(key) {
      return store[key];
    },
    setItem: function(key, value) {
      store[key] = value.toString();
    },
    clear: function() {
      store = {};
    }
  };
})();

Object.defineProperty(window, 'localStorage', { 
  value: mock,
});

Benutzer-Avatar
Benutzer123444555621

Gibt es irgendwelche Bibliotheken da draußen zu verspotten localStorage?

Ich habe nur eins geschrieben:

(function () {
    var localStorage = {};
    localStorage.setItem = function (key, val) {
         this[key] = val + '';
    }
    localStorage.getItem = function (key) {
        return this[key];
    }
    Object.defineProperty(localStorage, 'length', {
        get: function () { return Object.keys(this).length - 2; }
    });

    // Your tests here

})();

Meine ersten Tests zeigen, dass localStorage sich weigert, in Firefox zuweisbar zu sein

Nur im globalen Kontext. Mit einer Wrapper-Funktion wie oben funktioniert es einwandfrei.

Benutzer-Avatar
Konrad Calmez

Überschreiben der localStorage Eigentum des Globalen window Objekt, wie in einigen der Antworten vorgeschlagen, funktioniert in den meisten JS-Engines nicht, da sie deklarieren localStorage data-Eigenschaft als nicht beschreibbar und nicht konfigurierbar.

Ich habe jedoch herausgefunden, dass Sie zumindest mit der WebKit-Version von PhantomJS (Version 1.9.8) die Legacy-API verwenden können __defineGetter__ um zu kontrollieren, was passiert, wenn localStorage zugegriffen wird. Trotzdem wäre es interessant, ob das auch in anderen Browsern funktioniert.

var tmpStorage = window.localStorage;

// replace local storage
window.__defineGetter__('localStorage', function () {
    throw new Error("localStorage not available");
    // you could also return some other object here as a mock
});

// do your tests here    

// restore old getter to actual local storage
window.__defineGetter__('localStorage',
                        function () { return tmpStorage });

Der Vorteil dieses Ansatzes besteht darin, dass Sie den zu testenden Code nicht ändern müssen.

  • Ich habe gerade bemerkt, dass dies in PhantomJS 2.1.1 nicht funktioniert. 😉

    – Konrad Calmez

    12. Februar 2016 um 15:04 Uhr

1256530cookie-checkWie man localStorage in JavaScript-Einheitentests verspottet?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy