jestテストでlocalStorageを処理するにはどうすればよいですか?


144

私はJestテストで「localStorageが定義されていません」というメッセージを出し続けますが、これは理にかなっていますが、私の選択肢は何ですか?レンガの壁を打つ。

回答:


141

@chiedoの優れたソリューション

ただし、ES2015の構文を使用しているため、この方法で記述する方が少しクリーンだと感じました。

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
value + ''nullと未定義の値を正しく処理するために、おそらくセッターで行う必要があります
menehune23

|| null私のテストではを使用していたので、最新の冗談がちょうどそれを使用していたと思うので、私のテストは失敗しましたnot.toBeDefined()。@Chiedoソリューションが再び機能する
jcubic

私は、これは技術的にスタブだと思います:)嘲笑バージョンについてはこちらを参照してください。stackoverflow.com/questions/32911630/...
TigerBear

100

これの助けを借りてそれを理解しました:https ://groups.google.com/forum/#!topic/jestjs/9EPhuNWVYTg

次の内容のファイルをセットアップします。

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 });

次に、Jest設定の下のpackage.jsonに次の行を追加します

"setupTestFrameworkScriptFile":"PATH_TO_YOUR_FILE",


6
どうやら更新の1つでこのパラメーターの名前が変更され、「setupTestFrameworkScriptFile」と呼ばれるようになりました
Grzegorz Pawlik

2
"setupFiles": [...]同様に動作します。配列オプションを使用すると、モックを個別のファイルに分離できます。例:"setupFiles": ["<rootDir>/__mocks__/localStorageMock.js"]
Stiggler 2017年

3
の戻り値はgetItem、特定のキーに対してデータが設定されていない場合にブラウザから返される値とは少し異なります。getItem("foo")設定されていないときに呼び出すと、たとえばnullブラウザに戻りますがundefined、このモックによって-これにより、テストの1つが失敗しました。私にとっての簡単な解決策store[key] || nullは、getItem関数に戻ることでした
ベンブロードリー

これは次のような場合には機能しませんlocalStorage['test'] = '123'; localStorage.getItem('test')
rob

3
次のエラーが発生します-jest.fn()値はモック関数またはスパイである必要があります。何か案は?
ポールフィッツジェラルド

55

create-react-appを使用している場合は、ドキュメントで説明されているよりシンプルで簡単なソリューションがあります

これを作成してsrc/setupTests.js、その中に入れます。

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

以下のコメントでのトム・メルツの貢献:

次に、localStorageMockの関数が使用されていることを、次のようにテストできます。

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

呼び出されたことを確認したい場合は、テストの内部。https://facebook.github.io/jest/docs/en/mock-functions.htmlを確認してください


こんにちはc4k!テストでそれをどのように使用するか、例を挙げていただけますか?
Dimo 2017

どういう意味ですか ?テストで何も初期化する必要はありませんlocalStorage。コードで使用するものを自動的にモックするだけです。(あなたが使用しcreate-react-app、それが自然に提供するすべての自動スクリプトの場合)
c4k

呼び出されたことを確認したい場合は、テストの内部expect(localStorage.getItem).toBeCalledWith('token')またはexpect(localStorage.getItem.mock.calls.length).toBe(1)内部でlocalStorageMockの関数が使用されていることをテストできます。facebook.github.io/jest/docs/en/mock-functions.htmlを
Tom Mertz

10
このため、エラーが発生します-jest.fn()値はモック関数またはスパイである必要があります。何か案は?
ポールフィッツジェラルド

3
使用する複数のテストがある場合、これは問題を引き起こしませんlocalStorageか?各テストの後にスパイをリセットして、他のテストへの「スピルオーバー」を防止したくないですか?
ブランドンスタージョン

43

現在(10月19日)、localStorageは、通常のように、およびcreate-react-appのドキュメントで概説されているように、jestによってモックまたはスパイすることはできません。これは、jsdomで行われた変更が原因です。あなたはそれについてjestjsdom issue trackerで読むことができます。

回避策として、代わりにプロトタイプをスパイすることができます。

// 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();

実際には、spyOnを使用するだけで機能します。setItem関数をオーバーライドする必要はありませんjest.spyOn(window.localStorage.__proto__, 'setItem');
Yohan Dahmani

はい、2つを代替としてリストしました。両方を実行する必要はありません。
バスティアンスタイン

私は😉同様SetItem関数のオーバーライドすることなく、意味
ヨハンDahmani

理解できないと思います。明確にしていただけますか?
Bastian Stein

1
ああそう。一行目か二行目かどちらでも使えると言っていました。それらは同じことを行う代替手段です。個人的な好みが何であれ:)混乱については申し訳ありません。
Bastian Stein


13

undefined値を処理し(を持たないtoString())、null値が存在しない場合に戻る、より良い代替手段。これをreactv15でテストしreduxredux-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

追加するアイデアを提供してくれたAlexis Tylerに感謝removeItemdeveloper.mozilla.org/en-US/docs/Web/API/Storage/removeItem
Dmitriy

nullとundefinedが "null"と "undefined"(リテラル文字列)になる
必要があると考える

6

あなたがスタブではなくモックを探しているなら、これが私が使う解決策です:

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

初期化を簡単にするために、ストレージアイテムをエクスポートします。IE簡単にオブジェクトに設定できます

Jest + JSDomの新しいバージョンでは、これを設定することはできませんが、localstorageはすでに利用可能であり、次のようにスパイすることができます。

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

5

私はgithubからこの解決策を見つけました

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
});

このコードをsetupTestsに挿入すると、問題なく機能するはずです。

typesctiptを使用したプロジェクトでテストしました。


私にとってはObject.definePropertyがトリックを作りました。オブジェクトの直接割り当てが機能しませんでした。ありがとう!
Vicens Fayos、

4

残念ながら、ここで見つけた解決策は私にとってはうまくいきませんでした。

だから私はJest GitHubの問題を見ていて、これを見つけました スレッド

最も賛成された解決策はこれらのものでした:

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

// or

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

私のテストにはどちらもないwindowか、Storage定義されていません。多分それは私が使っているJestの古いバージョンでしょう。
Antrikshy

3

@ ck4が提案したドキュメントにlocalStorage、jest での使用に関する明確な説明があるためです。ただし、モック関数は、localStorageメソッドの。

以下は、データの書き込みと読み取りに抽象的なメソッドを使用する私のreactコンポーネントの詳細な例です。

//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 };

エラー:

TypeError: _setupLocalStorage2.default.setItem is not a function

修正:
冗談のためのモック機能の下に追加(パス:.jest/mocks/setUpStore.js

let mockStorage = {};

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

ここからスニペットを参照


2

Typescriptを使用するプロジェクトの問題を解決するために、ここで他のいくつかの回答をリフしました。私は次のようなLocalStorageMockを作成しました:

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]
    }
}

次に、LocalStorageWrapperクラスを作成しました。これは、グローバルローカルストレージ変数に直接アクセスする代わりに、アプリのローカルストレージへのすべてのアクセスに使用します。テスト用のラッパーにモックを簡単に設定できるようになりました。


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);

    });
});

モックを作成してglobalオブジェクトに追加する


2

このアプローチを使用して、モックを回避できます。

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

2

このスニペットでローカルストレージをモックする必要があります

// 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
});

そしてjest設定で:

"setupFiles":["localStorage.js"]

何でもお気軽にどうぞ。


1

次のソリューションは、より厳密なTypeScript、ESLint、TSLint、およびPrettier構成でのテストと互換性があります{ "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()

HT / https://stackoverflow.com/a/51583401/101290(global.localStorageの更新方法)


1

Typescriptで同じことを行うには、次のようにします。

次の内容のファイルをセットアップします。

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 });

次に、Jest設定の下のpackage.jsonに次の行を追加します

"setupTestFrameworkScriptFile":"PATH_TO_YOUR_FILE",

または、ローカルストレージをモックしたいテストケースにこのファイルをインポートします。


0

これは私のために働いた、

delete global.localStorage;
global.localStorage = {
getItem: () => 
 }
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.