ฉันได้รับ "localStorage ไม่ได้กำหนด" ในการทดสอบ Jest ที่เหมาะสม แต่มีทางเลือกอะไรบ้าง? ชนกำแพงอิฐ
ฉันได้รับ "localStorage ไม่ได้กำหนด" ในการทดสอบ Jest ที่เหมาะสม แต่มีทางเลือกอะไรบ้าง? ชนกำแพงอิฐ
คำตอบ:
ทางออกที่ดีจาก@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;
|| null
นั่นคือสาเหตุที่การทดสอบของฉันล้มเหลวเพราะในการทดสอบของฉันฉันใช้not.toBeDefined()
อยู่ วิธีการแก้ปัญหา @Chiedo ทำให้มันทำงานได้อีกครั้ง
คิดออกด้วยความช่วยเหลือจาก: 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 });
จากนั้นคุณเพิ่มบรรทัดต่อไปนี้ใน package.json ภายใต้ Jest configs ของคุณ
"setupTestFrameworkScriptFile":"PATH_TO_YOUR_FILE",
"setupFiles": [...]
ทำงานได้เช่นกัน ด้วยตัวเลือกอาร์เรย์ให้แยก mocks เป็นไฟล์แยก เช่น:"setupFiles": ["<rootDir>/__mocks__/localStorageMock.js"]
getItem
แตกต่างกันเล็กน้อยกับสิ่งที่จะถูกส่งกลับโดยเบราว์เซอร์หากไม่มีการตั้งค่าข้อมูลกับคีย์เฉพาะ การโทรgetItem("foo")
เมื่อไม่ได้ตั้งค่าจะส่งคืนตัวอย่างเช่นnull
ในเบราว์เซอร์ แต่undefined
ด้วยการเยาะเย้ยนี้ - นี่ทำให้การทดสอบหนึ่งของฉันล้มเหลว ทางออกที่ง่ายสำหรับฉันคือการที่จะกลับมาstore[key] || null
ในgetItem
ฟังก์ชั่น
localStorage['test'] = '123'; localStorage.getItem('test')
หากใช้แอป create-react-app จะมีวิธีแก้ไขที่ง่ายและตรงไปตรงมาในเอกสารเอกสาร
สร้างsrc/setupTests.js
และใส่สิ่งนี้ลงไป:
const localStorageMock = {
getItem: jest.fn(),
setItem: jest.fn(),
clear: jest.fn()
};
global.localStorage = localStorageMock;
Tom Mertz บริจาคในความคิดเห็นด้านล่าง:
จากนั้นคุณสามารถทดสอบว่าฟังก์ชั่น 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
localStorage
คุณใช้ในรหัสของคุณ (ถ้าคุณใช้create-react-app
และสคริปต์อัตโนมัติทั้งหมดจะมีให้ตามธรรมชาติ)
expect(localStorage.getItem).toBeCalledWith('token')
หรือexpect(localStorage.getItem.mock.calls.length).toBe(1)
ภายในการทดสอบของคุณถ้าคุณต้องการตรวจสอบให้แน่ใจว่ามันถูกเรียก ลองดูfacebook.github.io/jest/docs/en/mock-functions.html
localStorage
หรือไม่ คุณไม่ต้องการรีเซ็ตสายลับหลังจากการทดสอบแต่ละครั้งเพื่อป้องกัน "การล้น" ในการทดสอบอื่น ๆ หรือไม่?
ในปัจจุบัน (ต.ค. 19) localStorage ไม่สามารถล้อเลียนหรือสอดแนมโดยล้อเล่นอย่างที่คุณต้องการและตามที่ระบุไว้ในเอกสารสร้างแอปที่ตอบสนอง นี่คือเนื่องจากการเปลี่ยนแปลงใน jsdom คุณสามารถอ่านเกี่ยวกับเรื่องนี้ได้ในเครื่องมือติดตามปัญหาตลกและjsdom
คุณสามารถสอดแนมต้นแบบแทน:
// 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();
jest.spyOn(window.localStorage.__proto__, 'setItem');
หรือคุณเพียงแค่ใช้แพคเกจจำลองเช่นนี้:
https://www.npmjs.com/package/jest-localstorage-mock
มันจัดการไม่เพียง แต่ฟังก์ชั่นการจัดเก็บ แต่ยังช่วยให้คุณทดสอบว่าร้านค้าที่ถูกเรียกจริง
ทางเลือกที่ดีกว่าซึ่งจัดการundefined
ค่า (ไม่มีtoString()
) และส่งคืนnull
หากไม่มีค่า ทดสอบสิ่งนี้กับreact
v15 redux
และredux-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
removeItem
: developer.mozilla.org/en-US/docs/Web/API/Storage/removeItem
หากคุณกำลังมองหาการเยาะเย้ยและไม่ใช่ต้นขั้วนี่คือคำตอบที่ฉันใช้:
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');
ฉันพบโซลูชันนี้จาก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
น่าเสียดายที่โซลูชันที่ฉันพบที่นี่ไม่ได้ผลสำหรับฉัน
ดังนั้นฉันจึงดูปัญหา Jest GitHub และพบกระทู้นี้
โซลูชันที่มีการโหวตมากที่สุดคือโซลูชันเหล่านี้:
const spy = jest.spyOn(Storage.prototype, 'setItem');
// or
Storage.prototype.getItem = jest.fn(() => 'bla');
window
หรือStorage
กำหนดไว้เช่นกัน อาจเป็นเวอร์ชั่นเก่าของ Jest ที่ฉันใช้อยู่
ในฐานะที่เป็น @ ck4 เอกสารแนะนำมีคำอธิบายที่ชัดเจนสำหรับการใช้localStorage
ในการล้อเล่น อย่างไรก็ตามฟังก์ชั่นจำลองล้มเหลวในการดำเนินการlocalStorage
วิธีการใด ๆ
ด้านล่างนี้เป็นตัวอย่างโดยละเอียดขององค์ประกอบการตอบสนองของฉันซึ่งใช้ประโยชน์จากวิธีนามธรรมสำหรับการเขียนและอ่านข้อมูล
//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 = {}
};
มีการอ้างอิงตัวอย่างจากที่นี่
คุณสามารถใช้วิธีนี้เพื่อหลีกเลี่ยงการเยาะเย้ย
Storage.prototype.getItem = jest.fn(() => expectedPayload);
Riffed คำตอบอื่น ๆ ที่นี่เพื่อแก้ปัญหาสำหรับโครงการที่มี 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 ที่ฉันใช้สำหรับการเข้าถึงที่จัดเก็บในตัวเครื่องในแอพแทนการเข้าถึงตัวแปรที่จัดเก็บในตัวเครื่องโดยตรง ทำให้ง่ายต่อการตั้งค่าจำลองในเสื้อคลุมสำหรับการทดสอบ
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
objectt
คุณต้องจำลองที่เก็บข้อมูลในเครื่องด้วยตัวอย่างนี้
// 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
});
และในการกำหนดค่าล้อเล่น:
"setupFiles":["localStorage.js"]
อย่าลังเลที่จะถามอะไร
โซลูชันต่อไปนี้สามารถใช้งานร่วมกับการทดสอบด้วยการกำหนดค่า 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
หากต้องการทำสิ่งเดียวกันใน 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 });
จากนั้นคุณเพิ่มบรรทัดต่อไปนี้ใน package.json ภายใต้ Jest configs ของคุณ
"setupTestFrameworkScriptFile":"PATH_TO_YOUR_FILE",
หรือคุณนำเข้าไฟล์นี้ในกรณีทดสอบของคุณที่คุณต้องการเยาะเย้ย localstorage
สิ่งนี้ได้ผลสำหรับฉัน
delete global.localStorage;
global.localStorage = {
getItem: () =>
}
value + ''
ใน setter เพื่อจัดการค่า null และ undefined อย่างถูกต้อง