<p>jest.mock factory funktioniert nicht innerhalb eines Tests</p>

Erstellt am 12. Jan. 2017  ·  38Kommentare  ·  Quelle: facebook/jest

Möchten Sie eine Funktion anfordern oder einen Fehler melden?
Insekt

Wie ist das aktuelle Verhalten?

Es scheint, dass der Weg, einen Mock mit einer Fabrik zu erstellen, nicht innerhalb von test oder it funktioniert. Es funktioniert nur, wenn der Mock auf der Stammebene der Datei definiert ist.

Hier ist mein Beispiel für einen Mock:

jest.mock('services/feature', () => ({
    isEnabled: () => true
}));

Was ist das erwartete Verhalten?

Das Verspotten einer Datei in einem Test sollte funktionieren.

Bitte geben Sie Ihre genaue Jest-Konfiguration an und erwähnen Sie Ihre Jest-, Knoten-, Garn-/npm-Version und Ihr Betriebssystem.

Jest 18.0.0, Node 7.4, macOS

Confirmed Discussion

Hilfreichster Kommentar

Um den Rückgabewert eines Mocks zwischen Tests zu ändern, können Sie Folgendes tun:

jest.mock('whatever');

// Get the mock function
const whatever = require('whatever');

test('test 1', () => {
  whatever.mockImplementation(() => 'hello');
});

test('test 2', () => {
  whatever.mockImplementation(() => 'world');
});

Alle 38 Kommentare

jest.mock Aufrufe werden mit der babel-jest-Transformation automatisch an den Anfang der Datei gehievt. Sie können dieses Verhalten mit jest.doMock auslassen. Hast du das probiert?

Das gleiche mit doMock .

In den Dokumenten kann ich lesen

Hinweis: Bei Verwendung von babel-jest werden Aufrufe zum Mock automatisch an die Spitze des Codeblocks gehievt. Verwenden Sie doMock wenn Sie dieses Verhalten explizit vermeiden möchten.

Aber... Ein Test ist ein Codeblock, oder? In meinem Fall erwarte ich also keine Unterschiede.

Hier ist ein vollständiger Test

it('renders with the enabled feature', () => {
  jest.mock('services/feature', () => ({
      isEnabled: () => true
  }));

  const component = renderer.create(
      <MyComponent />
  );

  const tree = component.toJSON();
  expect(tree).toMatchSnapshot();
});

Könnten Sie eine Reproduktion davon in einem GH-Repository bereitstellen?

Sicher. Hier ist es: https://github.com/tleunen/jest-issue-2582

Beide Tests rendern "Disabled", obwohl einer von ihnen dann ein Mock hat, um stattdessen "Enabled" zu rendern.
Siehe diese:
https://github.com/tleunen/jest-issue-2582/blob/master/src/MyComponent.js
https://github.com/tleunen/jest-issue-2582/blob/master/src/__tests__/MyComponent.spec.js

Vielen Dank.

Irgendwelche Ratschläge @thymikee @cpojer für dieses Problem?
Ich habe mehrere Tests in derselben Datei und möchte für jeden von ihnen unterschiedliche Scheinantworten haben.

Ich bin froh, dass ich dieses Problem gefunden habe. Ich habe mir den Kopf zerbrochen, warum jest.mock() in meinem describe Bereich nicht funktioniert hat. Habe es nach oben verschoben (unter meine Importe in der Testdatei) und es funktioniert.

Für mich gilt es auch für jest.mock() ohne Fabrik, mit einem __mocks__ Ordner, der die verspottete Datei enthält.

--bearbeiten

Es ist offensichtlich notwendig, die jest.mock() Anweisung vor die Import-Anweisungen zu heben. @tleunen , dies bedeutet wahrscheinlich, dass es nicht möglich ist, dieselbe Datei mehr als einmal mit unterschiedlichen Antworten zu verspotten. Vielleicht sind Sie am besten gedient, wenn Sie die Factory-Funktion dynamischer gestalten, damit sie Ihnen in jedem Testfall unterschiedliche Ergebnisse liefern kann.

Meiner Meinung nach würde es deutlicher, wenn jest.mock() immer außerhalb der Blöcke describe und it . Aber das sollte dann in den Dokumenten klar angegeben werden

jest.mock wird also in den Funktionsbereich gehoben, deshalb funktioniert es nicht mit require s (und definitiv nicht mit import s, die in den Modulbereich gehoben werden), wenn Sie Rufen Sie es in einer anderen Funktion als describe (die von Jasmine speziell behandelt wird).
Ihr jest.mock Aufruf wird an die Spitze dieser Funktion (nicht des Moduls) gehievt, deshalb wird es nicht so funktionieren, wie Sie es erwarten.

Im Allgemeinen empfehlen wir, verschiedene Mocks in beforeEach und afterEach wenn Sie möchten, dass sie sich in den Testfällen unterscheiden.

@cpojer könnte dies im Detail erläutern und wenn wir die Anrufe in die oberen Bereiche heben möchten.

Dies liegt daran, dass Sie Ihre Module benötigen, wenn das Modul initialisiert wird (mithilfe von Import). jest.mock wird viel später angerufen. Der Weg dies zu lösen ist:

beforeEach(() => { // or the specific test
  jest.mock('MyModule', () => …);
  const MyModule = require('MyModule');
  …
});

usw.

Wenn Sie dies vor jedem tun würden, ist mir unklar, wie Sie Tests unterscheiden würden (wie würden Sie also für jeden Test einen anderen Mock geben?)

Das Einfügen in die Tests selbst funktioniert natürlich.

Können Sie dann ein simuliertes Modul innerhalb der getesteten Komponente passieren?

Was ist, wenn ich die Datei, die ich verspotten möchte, nicht importiere/erfordere (z. B. eine Abhängigkeit von einer anderen Datei, die ich importiere), aber ich möchte, dass sie auf einen describe/it-Block beschränkt ist? Oder auch wenn ich für den beforeEach/beforeAll Test anders verspotten möchte? Sind solche Fälle möglich?

// A.js depends on B.js
import A from './A';

describe('myTest', () => {

    describe('myFirstScope', () => {
        beforeAll(() => {
            jest.mock('./B', () => ({
                myFirstMethod: jest.fn(),
            }));
        });

        // tests here
    });

    describe('mySecondScope', () => {
        beforeAll(() => {
            jest.mock('./B', () => ({
                mySecondMethod: jest.fn(),
            }));
        });

        // tests here
    });
});

In diesem Fall müssen Sie A anfordern, nachdem Sie B verspottet haben (nicht import , sondern require ).

A verlangen, nachdem B verspottet wurde

hat bei mir nicht funktioniert, sah immer noch den ursprünglichen Mock von B zum Ergebnis durchgehen

@GoldAnna haben Sie eine Problemumgehung für dieses Problem gefunden?

@alayor habe ich nicht

So oft ich auf dieses Problem gestoßen bin, bin ich jetzt überzeugt, dass ich entweder nicht richtig teste, jest wie von den Autoren beabsichtigt verwende oder eine Kombination aus beidem verwende. Ein weiterer Beweis ist, dass mir fast keines der simulierten Beispiele in der Dokumentation von jest wie reale Beispiele vorkommt ... sie sind es wahrscheinlich und mein Ansatz ist wahrscheinlich falsch.

Das heißt, das Folgende funktioniert für mich und alle folgenden Tests bestehen. Beachten Sie, dass ich, um zur ursprünglichen Version von ModuleB , sowohl jest.resetModules() als auch jest.unmock('./moduleB') aufrufen muss ...die Reihenfolge spielt keine Rolle.

// Module A
const ModuleB = require('./moduleB');
const ModuleA = function() {
  this.title = new ModuleB().title;
  return this;
};
module.exports = ModuleA;

// Module B
const ModuleB = function() {
  this.title = 'Module B - Original'
  return this;
};
module.exports = ModuleB;

// Tests
describe('Jest a few tests', () => {
  it('should do something', () => {
    jest.resetModules();
    jest.mock('./moduleB', () => function() {
      this.title = 'Module B - Mock 1'
      return this;
    });
    const ModuleA = require('./moduleA');
    const moduleA = new ModuleA();
    expect(moduleA.title).toEqual('Module B - Mock 1');
  });

  it('should do something else', () => {
    jest.resetModules();
    jest.mock('./moduleB', () => function() {
      this.title = 'Module B - Mock 2'
      return this;
    });
    const ModuleA = require('./moduleA');
    const moduleA = new ModuleA();
    expect(moduleA.title).toEqual('Module B - Mock 2');
  });

  it('should do something original', () => {
    jest.resetModules();
    jest.unmock('./moduleB');
    const ModuleA = require('./moduleA');
    const moduleA = new ModuleA();
    expect(moduleA.title).toEqual('Module B - Original');
  });
});

Hey,

Bei mir funktioniert hier nichts.
Kann mir bitte jemand erklären, warum es nicht möglich ist, jest.mock darin zu verwenden?

Dankeschön

Ich denke, dies ist immer noch ein Problem, das vielleicht als Feature-Request erneut geöffnet werden sollte. Ich möchte Tests isoliert schreiben. Dinge in einen Mocks-Ordner werfen oder mit einem before neu verkabeln. Jeder endet normalerweise mit einer Junk-Schublade mit Mocks / seltsamen Dateninstanzen, die in jeden Test mitgeschleppt werden. Ich möchte einen kleinen Mock schreiben und einen kleinen Test bestehen.

Wir haben keine wirkliche Möglichkeit, einzelne Tests zu isolieren (in Anbetracht von test.concurrent ). Wenn wir eine API ähnlich wie tap oder ava übernehmen würden, wäre dies möglich, aber ich glaube nicht, dass dies mit den Einschränkungen möglich ist, die durch die aktuelle globale API auferlegt werden. Super glücklich, falsch bewiesen zu werden!

Hatte hier mit mehreren Methoden zu kämpfen, um einen zuvor in Mocha und Proxyquire geschriebenen Test umzugestalten, und endete damit, den Test in verschiedene Dateien für verschiedene Mocks aufzuteilen.

@gcox Haben Sie versucht, auf der obersten Ebene des Moduls zu spotten (und danach moduleA zu erfordern) und dann die Implementierung des Mocks für jeden Test zu ändern, der ein anderes Verhalten erfordert? Sie können dazu mockImplementation verwenden. So haben wir das in unseren Testsuiten gelöst.

@antgonzales Ich denke, das ist schwierig, wenn

@jkomusin Auf require('whatever') , einen anderen Mock über mehrere benachbarte Tests und nicht dasselbe Objekt zurückzugeben?".

Um den Rückgabewert eines Mocks zwischen Tests zu ändern, können Sie Folgendes tun:

jest.mock('whatever');

// Get the mock function
const whatever = require('whatever');

test('test 1', () => {
  whatever.mockImplementation(() => 'hello');
});

test('test 2', () => {
  whatever.mockImplementation(() => 'world');
});

Die Antwort von @SimenB hat auch funktioniert und ist so viel einfacher!

@rafaeleyng, aber @SimenB funktioniert nicht, wenn das, was Sie aus dem Modul exportieren, keine Funktion ist ...

Das @gcox- Beispiel enthält die meisten Informationen, nach denen ich in den letzten Stunden in den Dokumenten gesucht habe. Ich schlage vor, dass es in die offizielle Dokumentation aufgenommen werden sollte, die im Moment sowieso sehr spärlich ist.

@schumannd hat recht. Bitte machen Sie die offizielle Dokumentation klarer und knackiger.

PRs sind immer willkommen, die Dokumente zu verbessern 🙂

Modulbeispiel basierend auf
https://github.com/facebook/jest/issues/2582#issuecomment -378677440 ❤️

Heya, habe das über das verlinkte Problem gefunden und es herausgefunden:

jest.mock('child_process')
const childProcess = require('child_process')

describe('foo', () => {
  test('bar', () => {
    childProcess.execSync.mockImplentation(jest.fn().mockReturnValueOnce('wibble'))
    // code that itself requires child_process
    expect(childProcess.execSync.mock.results[0]).toEqual('wibble')
  })

  test('baz', () => {
    childProcess.execSync.mockImplentation(jest.fn().mockReturnValueOnce('wobble'))
    // code that itself requires child_process
    expect(childProcess.execSync.mock.results[0]).toEqual('wobble')
  })
})

Wenn Sie mehrere Funktionen/Methoden für den erforderlichen Code nachahmen müssen, müssen Sie sie separat einrichten, anstatt die Fabrik zu verwenden, wenn Sie dies global tun.

Wenn du wie ich nur eine einzelne Funktion brauchst, kannst du das Ganze vereinfachen:

jest.mock('child_process')
const { execSync } = require('child_process')

describe('foo', () => {
  test('bar', () => {
    execSync.mockImplentation(jest.fn().mockReturnValueOnce('wibble'))
    // code that itself requires child_process
    expect(execSync.mock.results[0]).toEqual('wibble')
  })

  test('baz', () => {
    execSync.mockImplentation(jest.fn().mockReturnValueOnce('wobble'))
    // code that itself requires child_process
    expect(execSync.mock.results[0]).toEqual('wobble')
  })
})

Was ist mit dem Verspotten von Abhängigkeiten von Nichtfunktionen wie JSON-Dateien oder zugeordneten Konstanten. Bitte machen Sie die Dokumentation klarer, wie man damit umgeht...

@gcox Ihre Lösung ist die einzige, die ich gefunden habe, wenn ein importiertes Modul im Test ein anderes Modul importiert, für das ich einen manuellen Mock in einem __mocks__ Ordner hatte.

Die Testdatei ruft beispielsweise jest.mock('./ModuleA') , die einen Schein in __mocks__/ModuleA.js . Aber ModulA wird nicht getestet, ModulB - das ModulA erfordert - ist das, was getestet wird. Ohne Ihre Lösung würde ModuleB die tatsächliche Implementierung von ModuleA erhalten, nicht das Mock.

Dies scheint ein wirklich seltsames Verhalten aus Scherz zu sein. Ich würde erwarten, dass, wenn ich jest.mock für ein Modul aufrufe, jedes getestete Modul, das eine Abhängigkeit von dem simulierten Modul hat, das Mock verwenden würde. Erscheint das bizarr, dass es so nicht funktioniert, oder gehe ich das komplett falsch an?

@SimenB In Ihrem Beispiel benötigen Sie das

@SimenB hat Ihre einfachere Lösung für mich gearbeitet. Vielen Dank! Wish konnte es früher finden, bevor ich Zeit mit vielen verschiedenen Wegen verbrachte

In meinem Fall habe ich den falschen Pfad zu dem Modul, das ich stuben wollte, verspottet und importiert. Ich hatte eine andere Groß-/Kleinschreibung im Dateipfad, also dachte mein Editor, dass es in Ordnung sei, aber es scheiterte einfach.

War

const { stubMethod } = require("./path/to/File");
jest.mock("./path/to/File");

Geändert in (Groß-/Kleinschreibung von File in file ändern):

const { stubMethod } = require("./path/to/file");
jest.mock("./path/to/file");

Hoffe das hilft jemand anderem.

Jasmine spyOn() hat kein solches Problem _inside_ Tests. Jest verwendet angeblich standardmäßig Jasmin? Warum funktioniert es nicht innerhalb des Tests?

Jasmin Beispiel:

spyOn(require('moduleB'), 'functionA').and.callFake(() => true);

Was ich beim Verspotten von Modulen nicht ganz verstehe, ist, dass es immer so beschrieben wird, als ob Sie das Modul hauptsächlich in Ihren Testfunktionen verwenden möchten. Wenn Sie sich beispielsweise den Beispielcode für die Funktion doMock in den Dokumenten ansehen:

test('moduleName 2', () => {
  jest.doMock('../moduleName', () => {
    return {
      __esModule: true,
      default: 'default2',
      foo: 'foo2',
    };
  });
  return import('../moduleName').then(moduleName => {
    expect(moduleName.default).toEqual('default2');
    expect(moduleName.foo).toEqual('foo2');
  });
});

In diesem Beispiel haben Sie nur innerhalb der Testfunktion Zugriff auf die neu verspottete Version von "moduleName" . Für mich ist es viel wichtiger, dass mein "Nicht-Test"-Code auch Zugriff auf die Mock-Version des Moduls hat. ZB eine Klasse, die ich in meinem Test verwende, die "moduleName" importiert hat, würde immer noch die Originalversion verwenden, nicht die verspottete.

Scheint verwandt zu sein: https://github.com/facebook/jest/issues/3236

Tipp - wenn Sie ein Modul haben, das einen primitiven Wert exportiert, zB:

```auth.js
export const isUserAdmin = getSetting('admin');

And you want to use a mock value instead, in the test, then a simple require and assignment seems to do the trick:

```deleteUser.js
const auth = require('../auth');

describe('deleteUser', () => {
  it('can delete if admin', () => {
    auth.isUserAdmin = true;

    // call method that depends on isUserAdmin value
    // and assert on the result
  });
});

Wenn Sie also ein Objekt simulieren möchten, das indirekt vom Code verwendet wird, den Sie testen, funktioniert die Funktion jest.doMock() nicht:

import myModuleToTest from './myModuleTotest'

describe('Given my module', () => {
  it('property1 will work as expect', () => {
    // Testing parts of the module that don't need to be mocked
  })

  it('property2 will work as expected', () => {
    jest.doMock('./myOtherModule', () => {
      return {
        __esModule: true,
        default: 'default2',
        foo: 'foo2',
      };
    });

    import('./myOtherModule').then(myOtherModule => {
      // I'm not interested on the mocked module myOtherModule but on the module that makes use of it
      myModuleToTest.doSomethingToSomeProperty(); // At this point myOtherModule's original module and not its mocked version will be used by myModuleToTest
      expect(myModuleToTest.someProperty).toBe('thisWillFail'); // The test won't pass because the mocked version wasn't used
    });
  });
});

Ab Jest 26 gibt es keine Möglichkeit mehr als einmal ein Modul zu verspotten, das ein Object exportiert, das indirekt verwendet wird (ich meine, etwas anderes als ein Function verspotten, da es kein mockFn.mockImplementation(fn) für verspottete Objects ). Ist das richtig oder übersehe ich etwas? Die einzige Lösung besteht dann darin, mehr als eine Testdatei zu haben, um dasselbe Modul zu testen.

Sie müssen myModuleToTest nach Ihrem Mock importieren, denn wenn es importiert wird
vor dem Schein macht keinen Sinn. Verwenden Sie also keinen Import .... oben oder
innerhalb des Rückrufs, weil es sowieso gehisst ist.

Am Di, 7. Juli 2020, 22:24 Antonio Redondo [email protected]
schrieb:

Wenn Sie also ein Objekt verspotten möchten, das indirekt von der
Code, den Sie mit der Funktion jest.doMock() testen, funktioniert nicht:

import myModuleToTest von './myModuleTotest'
it('funktioniert', () => {
jest.doMock('./myOtherModule', () => {
Rückkehr {
__esModule: wahr,
Vorgabe: 'Vorgabe2',
foo: 'foo2',
};
});

return import('../myOtherModule').then(myOtherModule => {
// Ich interessiere mich nicht für das verspottete Modul myOtherModule, sondern für das Modul, das es verwendet
myModuleToTest.doSomethingToSomeProperty(); // An dieser Stelle wird das Originalmodul von myOtherModule und nicht seine simulierte Version von myModuleToTest verwendet
Expect(myModuleToTest.someProperty).toBe('thisWillFail'); // Der Test wird nicht bestanden, weil die verspottete Version nicht verwendet wurde
});});

Ab Jest 26 gibt es keine Möglichkeit, ein Modul zu simulieren, das ein Objekt exportiert (I
etwas anderes als eine Funktion bedeuten), die indirekt verwendet wird. Ist das
richtig oder übersehe ich was? Die einzige Lösung ist dann mehr zu haben
als eine Testdatei, um dasselbe Modul zu testen.


Sie erhalten dies, weil Sie einen Kommentar abgegeben haben.
Antworten Sie direkt auf diese E-Mail und zeigen Sie sie auf GitHub an
https://github.com/facebook/jest/issues/2582#issuecomment-655110424 , oder
Abmelden
https://github.com/notifications/unsubscribe-auth/AAJR3UW6HARW44ZLKAUB7PLR2N77NANCNFSM4C4I7QSQ
.

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen