Jest testlerinde localStorage ile nasıl başa çıkarım?


144

Jest testlerinde "localStorage tanımlanmadı" elde etmeye devam ediyorum, bu mantıklı ama seçeneklerim neler? Tuğla duvarlara vurmak.

Yanıtlar:


142

@Chiedo'dan harika çözüm

Ancak, ES2015 sözdizimini kullanıyoruz ve bu şekilde yazmanın biraz daha temiz olduğunu hissettim.

class LocalStorageMock {
  constructor() {
    this.store = {};
  }

  clear() {
    this.store = {};
  }

  getItem(key) {
    return this.store[key] || null;
  }

  setItem(key, value) {
    this.store[key] = value.toString();
  }

  removeItem(key) {
    delete this.store[key];
  }
};

global.localStorage = new LocalStorageMock;

8
Muhtemelen value + ''null ve tanımsız değerleri doğru şekilde işlemek için ayarlayıcıda
yapmalıdır

En son jestin sadece bu || nullyüzden testimin başarısız olduğunu düşünüyorum çünkü testimde kullanıyordum not.toBeDefined(). @Chiedo çözümü tekrar çalışıyor
jcubic

Bu teknik olarak bir saplama olduğunu düşünüyorum :) alaycı sürüm için buraya bakın: stackoverflow.com/questions/32911630/…
TigerBear

100

Şunun yardımıyla çözdüm: https://groups.google.com/forum/#!topic/jestjs/9EPhuNWVYTg

Aşağıdaki içeriklere sahip bir dosya kurun:

var localStorageMock = (function() {
  var store = {};
  return {
    getItem: function(key) {
      return store[key];
    },
    setItem: function(key, value) {
      store[key] = value.toString();
    },
    clear: function() {
      store = {};
    },
    removeItem: function(key) {
      delete store[key];
    }
  };
})();
Object.defineProperty(window, 'localStorage', { value: localStorageMock });

Ardından, Jest yapılandırmalarınızın altındaki package.json dosyasına aşağıdaki satırı eklersiniz

"setupTestFrameworkScriptFile":"PATH_TO_YOUR_FILE",


6
Görünüşe göre güncellemelerden biri ile bu parametrenin adı değişti ve şimdi buna "setupTestFrameworkScriptFile" deniyor
Grzegorz Pawlik

2
"setupFiles": [...]çalışır. Dizi seçeneği ile alayları ayrı dosyalara ayırmaya izin verir. Örn:"setupFiles": ["<rootDir>/__mocks__/localStorageMock.js"]
Stiggler

3
getItemBelirli bir anahtara karşı herhangi bir veri ayarlanmadığı takdirde, dönüş değeri tarayıcı tarafından döndürülecek değerden biraz farklıdır. getItem("foo")ayarlanmadığında arama , örneğin nullbir tarayıcıda dönecektir , ancak undefinedbu sahte tarafından - bu, testlerimden birinin başarısız olmasına neden oluyordu. Benim için basit bir çözüm dönmek oldu store[key] || nullyılında getItemfonksiyonu
Ben Broadley

böyle bir şey yaparsanız bu işe yaramazlocalStorage['test'] = '123'; localStorage.getItem('test')
rob

3
Aşağıdaki hatayı alıyorum - jest.fn () değeri bir sahte işlev veya casus olmalıdır. Herhangi bir fikir?
Paul Fitzgerald

56

Create-tepki uygulaması kullanıyorsanız, dokümantasyonda açıklanan daha basit ve anlaşılır bir çözüm vardır .

src/setupTests.jsBunu oluşturun ve içine koyun:

const localStorageMock = {
  getItem: jest.fn(),
  setItem: jest.fn(),
  clear: jest.fn()
};
global.localStorage = localStorageMock;

Tom Mertz aşağıdaki yorumda katkıda bulundu:

Daha sonra localStorageMock işlevlerinin kullanıldığını test edebilirsiniz.

expect(localStorage.getItem).toBeCalledWith('token')
// or
expect(localStorage.getItem.mock.calls.length).toBe(1)

denendiğinden emin olmak istiyorsanız testlerinizin içinde. Check out https://facebook.github.io/jest/docs/en/mock-functions.html


Merhaba c4k! Bunu testlerinizde nasıl kullanacağınıza bir örnek verebilir misiniz?
Dimo

Ne demek istiyorsun ? Testlerinizde hiçbir şey başlatmanız gerekmez, sadece localStoragekodunuzda kullandığınız otomatik olarak alay eder . ( create-react-appdoğal olarak sağladığı tüm otomatik komut dosyalarını kullanırsanız )
c4k

Daha sonra , arandığından emin olmak istiyorsanız, testlerinizin içine expect(localStorage.getItem).toBeCalledWith('token')veya expect(localStorage.getItem.mock.calls.length).toBe(1)içine bir şey yaparak localStorageMock işlevlerinin kullanıldığını test edebilirsiniz. Check out facebook.github.io/jest/docs/en/mock-functions.html
Tom Mertz

10
Bunun için bir hata alıyorum - jest.fn () değeri bir sahte işlev veya casus olmalıdır. Herhangi bir fikir?
Paul Fitzgerald

3
Kullanan birden fazla testiniz varsa bu sorun yaratmaz mı localStorage? Diğer testlere "yayılmasını" önlemek için her testten sonra casusları sıfırlamak istemez misiniz?
Brandon Sturgeon

44

Şu anda (Ekim '19) localStorage, genellikle yaptığınız gibi ve oluştur-tepki-uygulama belgelerinde belirtildiği gibi jest tarafından alay edilemez veya casusluk edilemez. Bunun nedeni jsdom'da yapılan değişikliklerdir. Sen bu konuda okuyabilir jest ve jsdom sorunu izleyiciler.

Geçici bir çözüm olarak, prototip üzerinde casusluk yapabilirsiniz:

// does not work:
jest.spyOn(localStorage, "setItem");
localStorage.setItem = jest.fn();

// works:
jest.spyOn(window.localStorage.__proto__, 'setItem');
window.localStorage.__proto__.setItem = jest.fn();

// assertions as usual:
expect(localStorage.setItem).toHaveBeenCalled();

Aslında benim için sadece casus ile çalışıyor, setIt işlevini geçersiz kılmaya gerek yokjest.spyOn(window.localStorage.__proto__, 'setItem');
Yohan Dahmani

Evet, ikisini alternatif olarak listeledim, ikisini birden yapmaya gerek yok.
Bastian Stein

Ben de setItem geçersiz kılma olmadan demek oldu 😉
Yohan Dahmani

Anladığımı sanmıyorum. Lütfen açıklığa kavuşturabilir misiniz?
Bastian Stein

1
Ah evet. İlk satırı veya ikinci satırı kullanabileceğinizi söylüyordum. Aynı şeyi yapan alternatiflerdir. Kişisel tercihiniz ne olursa olsun :) Karışıklık için özür dileriz.
Bastian Stein


13

undefinedDeğerleri (varsa toString()) işleyen ve nulldeğer yoksa döndüren daha iyi bir alternatif . Bunu reactv15 ile test ettik reduxveredux-auth-wrapper

class LocalStorageMock {
  constructor() {
    this.store = {}
  }

  clear() {
    this.store = {}
  }

  getItem(key) {
    return this.store[key] || null
  }

  setItem(key, value) {
    this.store[key] = value
  }

  removeItem(key) {
    delete this.store[key]
  }
}

global.localStorage = new LocalStorageMock

Eklemek için Alexis Tyler'a teşekkürler removeItem: developer.mozilla.org/en-US/docs/Web/API/Storage/removeItem
Dmitriy

Null ve undefined'e "null" ve "undefined" (değişmez dizeler) ile sonuçlanması gerektiğine inan
menehune23 15

6

Eğer bir saplama değil, bir sahte arıyorsanız, işte kullandığım çözüm:

export const localStorageMock = {
   getItem: jest.fn().mockImplementation(key => localStorageItems[key]),
   setItem: jest.fn().mockImplementation((key, value) => {
       localStorageItems[key] = value;
   }),
   clear: jest.fn().mockImplementation(() => {
       localStorageItems = {};
   }),
   removeItem: jest.fn().mockImplementation((key) => {
       localStorageItems[key] = undefined;
   }),
};

export let localStorageItems = {}; // eslint-disable-line import/no-mutable-exports

Depolama öğelerini kolay başlatma için dışa aktarıyorum. IE Kolayca bir nesneye ayarlayabilirsiniz

Jest + JSDom'un daha yeni sürümlerinde bunu ayarlamak mümkün değildir, ancak localstorage zaten mevcuttur ve üzerinde casusluk yapabilirsiniz:

const setItemSpy = jest.spyOn(Object.getPrototypeOf(window.localStorage), 'setItem');

5

Bu çözümü github'dan buldum

var localStorageMock = (function() {
  var store = {};

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

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

Bu kodu setupTest'lerinize ekleyebilirsiniz ve düzgün çalışması gerekir.

Ben typesctipt ile bir projede test etti.


benim için Object.defineProperty hile yaptı. Doğrudan nesne atama işe yaramadı. Teşekkürler!
Vicens Fayos

4

Ne yazık ki, burada bulduğum çözümler benim için işe yaramadı.

Bu yüzden Jest GitHub sorunlarına bakıyordum ve bu konuyu buldum

En çok oylanan çözümler şunlardı:

const spy = jest.spyOn(Storage.prototype, 'setItem');

// or

Storage.prototype.getItem = jest.fn(() => 'bla');

Testlerim de tanımlanmamış windowveya Storagetanımlanmamış. Belki de Jest'in eski versiyonudur.
Antrikshy

3

@ Ck4 önerdiği belgelerinlocalStorage jest olarak kullanılması konusunda açık bir açıklaması vardır . Ancak, sahte işlevler,localStorage yöntemlerin .

Aşağıda, veri yazma ve okuma için soyut yöntemleri kullanan tepki bileşenimin ayrıntılı örneği,

//file: storage.js
const key = 'ABC';
export function readFromStore (){
    return JSON.parse(localStorage.getItem(key));
}
export function saveToStore (value) {
    localStorage.setItem(key, JSON.stringify(value));
}

export default { readFromStore, saveToStore };

Hata:

TypeError: _setupLocalStorage2.default.setItem is not a function

Düzeltme:
jest için sahte fonksiyonu aşağıda Ekle (yol: .jest/mocks/setUpStore.js)

let mockStorage = {};

module.exports = window.localStorage = {
  setItem: (key, val) => Object.assign(mockStorage, {[key]: val}),
  getItem: (key) => mockStorage[key],
  clear: () => mockStorage = {}
};

Snippet'e buradan referans verilir


3

Alaydan kaçınmak için bu yaklaşımı kullanabilirsiniz.

Storage.prototype.getItem = jest.fn(() => expectedPayload);

2

Typescript ile bir proje için çözmek için burada bazı diğer cevaplar attı. Böyle bir LocalStorageMock oluşturdum:

export class LocalStorageMock {

    private store = {}

    clear() {
        this.store = {}
    }

    getItem(key: string) {
        return this.store[key] || null
    }

    setItem(key: string, value: string) {
        this.store[key] = value
    }

    removeItem(key: string) {
        delete this.store[key]
    }
}

Daha sonra, global yerel depolama değişkenine doğrudan erişmek yerine uygulamadaki yerel depolamaya tüm erişim için kullandığım bir LocalStorageWrapper sınıfı oluşturdum. Testler için alaycı sargıya yerleştirmeyi kolaylaştırdı.


2
    describe('getToken', () => {
    const Auth = new AuthService();
    const token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6Ik1yIEpvc2VwaCIsImlkIjoiNWQwYjk1Mzg2NTVhOTQ0ZjA0NjE5ZTA5IiwiZW1haWwiOiJ0cmV2X2pvc0Bob3RtYWlsLmNvbSIsInByb2ZpbGVVc2VybmFtZSI6Ii9tcmpvc2VwaCIsInByb2ZpbGVJbWFnZSI6Ii9Eb3Nlbi10LUdpci1sb29rLWN1dGUtbnVrZWNhdDMxNnMtMzExNzAwNDYtMTI4MC04MDAuanBnIiwiaWF0IjoxNTYyMzE4NDA0LCJleHAiOjE1OTM4NzYwMDR9.YwU15SqHMh1nO51eSa0YsOK-YLlaCx6ijceOKhZfQZc';
    beforeEach(() => {
        global.localStorage = jest.fn().mockImplementation(() => {
            return {
                getItem: jest.fn().mockReturnValue(token)
            }
        });
    });
    it('should get the token from localStorage', () => {

        const result  = Auth.getToken();
        expect(result).toEqual(token);

    });
});

Bir alay oluşturma ve bunu globalnesneye ekleme


2

Bu snippet'lerle yerel depolamayı taklit etmeniz gerekiyor

// localStorage.js

var localStorageMock = (function() {
    var store = {};

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

})();

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

Ve jest yapılandırmasında:

"setupFiles":["localStorage.js"]

Herhangi bir şey sormaktan çekinmeyin.


1

Aşağıdaki çözüm daha katı TypeScript, ESLint, TSLint ve Prettier yapılandırması ile test için uyumludur { "proseWrap": "always", "semi": false, "singleQuote": true, "trailingComma": "es5" }:

class LocalStorageMock {
  public store: {
    [key: string]: string
  }
  constructor() {
    this.store = {}
  }

  public clear() {
    this.store = {}
  }

  public getItem(key: string) {
    return this.store[key] || undefined
  }

  public setItem(key: string, value: string) {
    this.store[key] = value.toString()
  }

  public removeItem(key: string) {
    delete this.store[key]
  }
}
/* tslint:disable-next-line:no-any */
;(global as any).localStorage = new LocalStorageMock()

Global.localStorage'ın nasıl güncelleneceği hakkında HT / https://stackoverflow.com/a/51583401/101290


1

Aynı şeyi Dizeler yazısında yapmak için aşağıdakileri yapın:

Aşağıdaki içeriklere sahip bir dosya kurun:

let localStorageMock = (function() {
  let store = new Map()
  return {

    getItem(key: string):string {
      return store.get(key);
    },

    setItem: function(key: string, value: string) {
      store.set(key, value);
    },

    clear: function() {
      store = new Map();
    },

    removeItem: function(key: string) {
        store.delete(key)
    }
  };
})();
Object.defineProperty(window, 'localStorage', { value: localStorageMock });

Ardından, Jest yapılandırmalarınızın altındaki package.json dosyasına aşağıdaki satırı eklersiniz

"setupTestFrameworkScriptFile":"PATH_TO_YOUR_FILE",

Veya bu dosyayı localstorage ile alay etmek istediğiniz test vakanıza aktarırsınız.


0

Bu benim için çalıştı,

delete global.localStorage;
global.localStorage = {
getItem: () => 
 }
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.