Dies ist meine erste Erfahrung mit Front-End-Tests. In diesem Projekt verwende ich Jest-Snapshot-Tests und habe einen Fehler TypeError: window.matchMedia is not a function
in meiner Komponente erhalten.
Ich habe die Jest-Dokumentation durchgesehen und den Abschnitt "Manuelle Verspottungen" gefunden, aber ich habe noch keine Ahnung, wie das geht.
addListener
undremoveListener
veraltet sindaddEventListener
undremoveEventListener
stattdessen verwendet werden sollten. Das vollständigeIch habe diese Technik verwendet, um eine Reihe von Spottproblemen zu lösen.
describe("Test", () => { beforeAll(() => { Object.defineProperty(window, "matchMedia", { writable: true, value: jest.fn().mockImplementation(query => ({ matches: false, media: query, onchange: null, addListener: jest.fn(), // Deprecated removeListener: jest.fn(), // Deprecated addEventListener: jest.fn(), removeEventListener: jest.fn(), dispatchEvent: jest.fn(), })) }); }); });
Oder, wenn Sie es die ganze Zeit verspotten möchten, können Sie in Ihre
mocks
Datei einfügen, die von Ihrem aufgerufen wirdpackage.json
:"setupFilesAfterEnv": "<rootDir>/src/tests/mocks.js",
.Referenz: setupTestFrameworkScriptFile
quelle
TypeError: Cannot read property 'matches' of undefined
AusnahmesetupFilesAfterEnv
und nichtsetupFiles
?Ich habe einen matchMedia- Stub in meine Jest-Testdatei (über den Tests) eingefügt , damit die Tests bestanden werden können:
window.matchMedia = window.matchMedia || function() { return { matches: false, addListener: function() {}, removeListener: function() {} }; };
quelle
global.window.matchMedia = jest.fn(() => { return { matches: false, addListener: jest.fn(), removeListener: jest.fn() } })
Jest verwendet jsdom , um eine Browserumgebung zu erstellen. JSDom unterstützt dies jedoch nicht,
window.matchMedia
sodass Sie es selbst erstellen müssen.Jests manuelle Mocks funktionieren mit Modulgrenzen, dh erfordern / importieren Anweisungen, damit sie nicht so verspottet werden können,
window.matchMedia
wie sie sind, weil sie global sind.Sie haben also zwei Möglichkeiten:
Definieren Sie Ihr eigenes lokales matchMedia-Modul, das window.matchMedia exportiert. - Auf diese Weise können Sie ein manuelles Modell definieren, das in Ihrem Test verwendet werden soll.
Definieren Sie eine Setup-Datei, die dem globalen Fenster einen Mock für matchMedia hinzufügt.
Mit einer dieser Optionen können Sie eine matchMedia-Polyfüllung als Mock verwenden, mit der zumindest Ihre Tests ausgeführt werden können, oder wenn Sie verschiedene Zustände simulieren müssen, möchten Sie möglicherweise Ihre eigenen mit privaten Methoden schreiben, mit denen Sie das Verhalten ähnlich dem konfigurieren können Scherz
fs
manuell verspottenquelle
Ich bin gerade auf dieses Problem gestoßen und musste diese in jestGlobalMocks.ts verspotten:
Object.defineProperty(window, 'matchMedia', { value: () => { return { matches: false, addListener: () => {}, removeListener: () => {} }; } }); Object.defineProperty(window, 'getComputedStyle', { value: () => { return { getPropertyValue: () => {} }; } });
quelle
Sie können die API verspotten:
describe("Test", () => { beforeAll(() => { Object.defineProperty(window, "matchMedia", { value: jest.fn(() => { return { matches: true, addListener: jest.fn(), removeListener: jest.fn() }; }) }); }); });
quelle
Sie können das
jest-matchmedia-mock
Paket zum Testen von Medienabfragen verwenden (z. B. Änderung des Gerätebildschirms, Änderung des Farbschemas usw.).quelle
Ich habe alle oben genannten vorherigen Antworten ohne Erfolg ausprobiert.
Das Hinzufügen von matchMedia.js zum Mocks- Ordner hat es für mich getan.
Ich habe es mit dem Inhalt von techguy2000 gefüllt :
// __mocks__/matchMedia.js 'use strict'; Object.defineProperty(window, 'matchMedia', { value: () => ({ matches: false, addListener: () => {}, removeListener: () => {} }) }); Object.defineProperty(window, 'getComputedStyle', { value: () => ({ getPropertyValue: () => {} }) }); module.exports = window;
Und dann importiert dies in
setup.js
:import matchMedia from '../__mocks__/matchMedia';
Boom! :) :)
quelle
Diese Jungs haben eine ziemlich raffinierte Lösung über die Jest setupFiles:
https://github.com/HospitalRun/components/pull/117/commits/210d1b74e4c8c14e1ffd527042e3378bba064ed8
quelle
TL; DR Antwort weiter unten
In meinem Fall war die Antwort nicht genug, da
window.matchMedia
sie immer zurückkehren würdefalse
(odertrue
wenn Sie sie ändern). Ich hatte einige React-Hooks und -Komponenten, die mehrere verschiedene Abfragen mit möglicherweise unterschiedlichen abhören musstenmatches
.Was ich versucht habe
Wenn Sie jeweils nur eine Abfrage testen müssen und Ihre Tests nicht auf mehreren Übereinstimmungen beruhen,
jest-matchmedia-mock
war dies hilfreich. Nach dem Versuch, es 3 Stunden lang zu verwenden, habe ich jedoch verstandenuseMediaQuery
, dass die vorherigen Abfragen, die Sie gestellt haben , beim Anrufen nicht mehr funktionieren. Tatsächlich stimmt die Abfrage, an die Sie übergebenuseMediaQuery
, nur dann überein,true
wenn Ihr Codewindow.matchMedia
dieselbe Abfrage aufruft , unabhängig von der tatsächlichen "Fensterbreite".Antworten
Nachdem mir klar wurde, dass ich meine Abfragen nicht testen konnte
jest-matchmedia-mock
, änderte ich die ursprüngliche Antwort ein wenig, um das Verhalten dynamischer Abfragen verspotten zu könnenmatches
. Diese Lösung erfordert dascss-mediaquery
npm-Paket.import mediaQuery from "css-mediaquery"; // Mock window.matchMedia's impl. Object.defineProperty(window, "matchMedia", { writable: true, value: jest.fn().mockImplementation((query) => { const instance = { matches: mediaQuery.match(query, { width: window.innerWidth, height: window.innerHeight, }), media: query, onchange: null, addListener: jest.fn(), // Deprecated removeListener: jest.fn(), // Deprecated addEventListener: jest.fn(), removeEventListener: jest.fn(), dispatchEvent: jest.fn(), }; // Listen to resize events from window.resizeTo and update the instance's match window.addEventListener("resize", () => { const change = mediaQuery.match(query, { width: window.innerWidth, height: window.innerHeight, }); if (change != instance.matches) { instance.matches = change; instance.dispatchEvent("change"); } }); return instance; }), }); // Mock window.resizeTo's impl. Object.defineProperty(window, "resizeTo", { value: (width: number, height: number) => { Object.defineProperty(window, "innerWidth", { configurable: true, writable: true, value: width, }); Object.defineProperty(window, "outerWidth", { configurable: true, writable: true, value: width, }); Object.defineProperty(window, "innerHeight", { configurable: true, writable: true, value: height, }); Object.defineProperty(window, "outerHeight", { configurable: true, writable: true, value: height, }); window.dispatchEvent(new Event("resize")); }, });
Es wird
css-mediaquery
mit dem verwendetwindow.innerWidth
, um zu bestimmen, ob die Abfrage tatsächlich mit einem fest codierten Booleschen Wert übereinstimmt. Außerdem wird die Größe von Ereignissen abgehört, die von derwindow.resizeTo
verspotteten Implementierung ausgelöst wurden , um denmatches
Wert zu aktualisieren .Sie können jetzt
window.resizeTo
in Ihren Tests die Breite des Fensters ändern, damit Ihre Aufrufewindow.matchMedia
diese Breite widerspiegeln. Hier ist ein Beispiel, das nur für diese Frage erstellt wurde. Ignorieren Sie also die Leistungsprobleme!const bp = { xs: 200, sm: 620, md: 980, lg: 1280, xl: 1920 }; // Component.tsx const Component = () => { const isXs = window.matchMedia(`(min-width: ${bp.xs}px)`).matches; const isSm = window.matchMedia(`(min-width: ${bp.sm}px)`).matches; const isMd = window.matchMedia(`(min-width: ${bp.md}px)`).matches; const isLg = window.matchMedia(`(min-width: ${bp.lg}px)`).matches; const isXl = window.matchMedia(`(min-width: ${bp.xl}px)`).matches; console.log("matches", { isXs, isSm, isMd, isLg, isXl }); const width = (isXl && "1000px") || (isLg && "800px") || (isMd && "600px") || (isSm && "500px") || (isXs && "300px") || "100px"; return <div style={{ width }} />; }; // Component.test.tsx it("should use the md width value", () => { window.resizeTo(bp.md, 1000); const wrapper = mount(<Component />); const div = wrapper.find("div").first(); // console.log: matches { isXs: true, isSm: true, isMd: true, isLg: false, isXl: false } expect(div.prop("style")).toHaveProperty("width", "600px"); });
Hinweis: Ich habe das Verhalten beim Ändern der Fenstergröße NACH der Montage der Komponente nicht getestet
quelle