Jest: Bagaimana cara mengejek fungsi modul tertentu?

Dibuat pada 25 Apr 2016  ·  116Komentar  ·  Sumber: facebook/jest

Saya berjuang dengan sesuatu yang saya pikir seharusnya mudah dan jelas, tetapi untuk alasan apa pun saya tidak dapat mengetahuinya.

Saya punya modul. Ini mengekspor beberapa fungsi. Ini myModule.js :

export function foo() {...}
export function bar() {...}
export function baz() {...}

Saya membuka modul untuk pengujian.

jest.unmock('./myModule.js');

Namun, saya perlu mengejek foo , karena itu membuat panggilan ajax ke backend saya. Saya ingin setiap fungsi dalam file ini tetap tidak dicemooh, mengharapkan foo , yang saya ingin diejek. Dan fungsi bar dan baz melakukan panggilan secara internal ke foo , jadi ketika pengujian saya memanggil bar() , bar dipalsukan akan memanggil mengejek foo .

Tampaknya dalam dokumentasi lelucon bahwa panggilan ke unmock dan mock beroperasi di seluruh modul. Bagaimana saya bisa mengejek fungsi tertentu? Memecah kode saya secara sewenang-wenang menjadi modul terpisah sehingga dapat diuji dengan benar adalah konyol.

Komentar yang paling membantu

Anda dapat melakukan:

jest.unmock('./myModule.js');

const myModule = require('myModule');
myModule.foo = jest.fn();

Lihat http://facebook.github.io/jest/docs/api.html#mock -functions

Semua 116 komentar

Setelah analisis lebih dalam, tampaknya jest-mock menghasilkan AST untuk seluruh modul, kemudian menggunakan AST itu untuk membuat modul tiruan yang sesuai dengan ekspor asli: https://github.com/facebook/jest/tree/master/packages /jest-mock

Kerangka kerja pengujian lainnya, seperti tiruan Python (https://docs.python.org/3/library/unittest.mock-examples.html), memungkinkan Anda meniru fungsi tertentu. Ini adalah konsep pengujian mendasar.

Saya sangat merekomendasikan kemampuan untuk mengejek sebagian dari modul. Saya pikir jest-mock harus diubah untuk mengabaikan ekspor dari ejekan secara kondisional, dan merujuk implementasi aslinya.

Anda dapat melakukan:

jest.unmock('./myModule.js');

const myModule = require('myModule');
myModule.foo = jest.fn();

Lihat http://facebook.github.io/jest/docs/api.html#mock -functions

Saya pikir Anda memiliki kesalahpahaman mendasar tentang cara kerja require . Saat Anda memanggil require() , Anda tidak mendapatkan instance dari modul. Anda mendapatkan objek dengan referensi ke fungsi modul. Jika Anda menimpa nilai dalam modul yang diperlukan, referensi Anda sendiri akan ditimpa, _tetapi implementasinya tetap menggunakan referensi asli_.

Dalam contoh Anda, jika Anda memanggil myModule.foo() , ya, Anda akan memanggil versi tiruan. Tetapi jika Anda memanggil myModule.bar() , yang secara internal memanggil foo() , foo yang dirujuk _bukan versi Anda yang ditimpa_. Jika Anda tidak percaya, Anda dapat mengujinya.

Oleh karena itu, contoh yang Anda jelaskan tidak memadai untuk masalah yang saya miliki. Apakah Anda tahu sesuatu yang saya tidak?

@cpojer

Saya percaya saya memahami ini dengan cukup baik. Namun cara babel mengkompilasi modul tidak membuat ini lebih mudah dipahami dan saya memahami kebingungan Anda. Saya tidak tahu persis bagaimana ini akan berperilaku di lingkungan ES2015 nyata dengan modul, terutama karena tidak ada lingkungan seperti itu saat ini (kecuali mungkin versi terbaru Chrome Canary, yang belum saya coba). Untuk menjelaskan apa yang terjadi, kita harus melihat hasil kompilasi dari kode babel Anda. Ini akan terlihat seperti ini:

var foo = function foo() {};
var bar = function bar() { foo(); };

exports.foo = foo;
exports.bar = bar;

Dalam hal ini, memang benar bahwa Anda tidak dapat mengejek foo dan saya minta maaf karena tidak membaca masalah awal Anda dengan benar, namun itu tidak membuat asumsi tentang bagaimana foo dipanggil, jadi saya berasumsi itu adalah exports.foo() . Mendukung hal di atas dengan mengejek suatu fungsi setelah membutuhkan modul tidak mungkin dilakukan dalam JavaScript - ada (hampir) tidak ada cara untuk mengambil pengikatan yang dirujuk foo dan memodifikasinya.

Namun, jika Anda mengubah kode Anda menjadi ini:

var foo = function foo() {};
var bar = function bar() { exports.foo(); };

exports.foo = foo;
exports.bar = bar;

dan kemudian di file pengujian Anda, Anda melakukan:

var module = require('../module');
module.foo = jest.fn();
module.bar();

itu akan bekerja seperti yang diharapkan. Inilah yang kami lakukan di Facebook di mana kami tidak menggunakan ES2015.

Sementara modul ES2015 mungkin memiliki binding yang tidak dapat diubah untuk apa yang mereka ekspor, kode terkompilasi yang mendasari yang dikompilasi oleh babel saat ini tidak memberlakukan batasan seperti itu. Saya tidak melihat cara saat ini untuk mendukung persis apa yang Anda minta dalam lingkungan modul ES2015 yang ketat dengan modul yang didukung secara asli. Cara kerja jest-mock adalah menjalankan kode modul secara terpisah dan kemudian mengambil metadata modul dan membuat fungsi tiruan. Sekali lagi, dalam hal ini tidak akan ada cara untuk mengubah pengikatan lokal foo . Jika Anda memiliki ide tentang cara menerapkan ini secara efektif, silakan berkontribusi di sini atau dengan permintaan tarik. Saya ingin mengingatkan Anda bahwa kami memiliki kode etik untuk proyek ini yang dapat Anda baca di sini: https://code.facebook.com/pages/876921332402685/open-source-code-of-conduct

Solusi yang tepat dalam contoh Anda bukanlah untuk mengejek foo tetapi untuk mengejek API tingkat tinggi yang dipanggil foo (seperti XMLHttpRequest atau abstraksi yang Anda gunakan sebagai gantinya).

@cpojer Terima kasih atas penjelasan rinci Anda. Saya minta maaf jika saya menyinggung Anda dengan bahasa saya, saya sangat efisien dengan tulisan teknik saya dan saya ingin menyampaikan maksud saya secepatnya. Untuk menempatkan segala sesuatunya ke dalam perspektif, saya menghabiskan 5 jam waktu mencoba memahami masalah ini dan menulis 2 komentar terperinci, lalu Anda menutupnya dengan pesan singkat yang sama sekali tidak memahami inti dari kedua pernyataan saya. Itulah mengapa pesan saya berikutnya mengatakan Anda memiliki "kesalahpahaman mendasar", karena 1) Anda tidak mengerti poin yang saya buat, atau 2) Anda tidak mengerti require() , yang untungnya adalah opsi 1.

Saya akan merenungkan solusi yang mungkin untuk masalah saya, untuk menyiasatinya untuk saat ini saya mengejek API tingkat yang lebih rendah, tetapi pasti harus ada cara untuk mengejek fungsi secara langsung, karena itu akan sangat berguna.

Saya setuju akan berguna untuk dapat melakukan ini, tetapi tidak ada cara yang baik di JS untuk melakukannya tanpa (mungkin lambat) analisis statis di muka :(

@cpojer : Saya tidak yakin apakah melompat ke sini 5 bulan kemudian adalah cara yang harus dilakukan tetapi saya tidak dapat menemukan percakapan lain tentang ini.

Melompat dari saran Anda di atas, saya telah melakukan ini untuk mengejek satu fungsi dari yang lain dalam modul yang sama:

jest.unmock('./someModule.js');
import someModule from './someModule.js';

it('function1 calls function 2', () => {
    someModule.function2 = jest.fn();

    someModule.function1(...);

    expect(someModule.function2).toHaveBeenCalledWith(...);
});

Ini berfungsi untuk satu tes, tetapi saya belum menemukan cara untuk mencapai ini dengan cara yang terisolasi hanya pada satu blok it(...); . Seperti yang ditulis di atas, ini mempengaruhi setiap tes yang membuatnya sulit untuk menguji function2 di tes lain. Ada tips?

Anda dapat memanggil .mockClear pada fungsi di beforeEach atau memanggil jest.clearAllMocks() jika Anda menggunakan Jest 16.

Hai @cpojer! Saya menggunakan Jest 16. Baik jest.clearAllMocks() atau someModule.function2.mockClear() berfungsi untuk saya. Mereka hanya berfungsi ketika tiruan adalah seluruh modul, bukan fungsi dari modul yang diimpor. Dalam proyek saya, fungsi tersebut tetap diejek dalam pengujian berikutnya. Jika ini tidak diharapkan, saya akan melihat apakah saya dapat mereplikasi dalam proyek sampel kecil dan membuat masalah baru. Ide bagus?

@cpojer -

Solusi yang tepat dalam contoh Anda bukanlah untuk mengejek foo tetapi untuk mengejek API tingkat tinggi yang dipanggil foo (seperti XMLHttpRequest atau abstraksi yang Anda gunakan sebagai gantinya).

Saya baru mengenal Jest dan saya berjuang dengan masalah yang sama. Saya menggunakan axios , yang di bawah tenda menggunakan XMLHttpRequest , dan saya tidak ingin mengejek axios , tetapi untuk mengejek XMLHttpRequest . Sepertinya saya harus mengimplementasikan metodenya sendiri, kira-kira seperti ini . Apakah ini pendekatan yang tepat?

Terima kasih!

ya, sesuatu seperti ini seharusnya membawa Anda ke jalan yang benar! :) Gunakan jest.fn sebagai API yang lebih bagus :D

@cpojer mengenai komentar Anda di sini: https://github.com/facebook/jest/issues/936#issuecomment -214939935

Bagaimana Anda melakukannya dengan ES2015?

// myModyle.js
export foo = (string) => "foo-" + string
export bar = (string2) => foo(string2)

// myModule.test.js
var module = require('./myModule');

// how do I mock foo here so this test passes?
expect(bar("hello")).toEqual("test-hello")

Bagi siapa saja yang menemukan ini mencari solusi, berikut ini tampaknya berfungsi untuk saya ketika mengekspor banyak const/fungsi dalam satu file, dan mengimpornya dalam file yang saya uji

`` javascript function mockFunctions() { const original = require.requireActual('../myModule'); return { ...original, //Pass down all the exported objects test: jest.fn(() => {console.log('I didnt call the original')}), someFnIWantToCurry: {console.log('I will curry the original') return jest.fn((...args) => original.someFnIWantToCurry(...args)}), } jest.mock('../myModule', () => mockFunctions()); const storage = require.requireMock('../myModule');

@ainesophaur , tidak yakin apa yang saya lakukan salah di sini. Tapi sepertinya itu tidak berhasil
Saat ini saya menggunakan lelucon 18.1 (dan create-react-app 0.9.4)

...<codes from comment above>..

// Let's say the original myModule has a function doSmth() that calls test()
storage.doSmth();
expect(storage.test).toHaveBeenCalled();

Tes kemudian akan gagal dengan:

expect(jest.fn()).toHaveBeenCalled()
Expected mock function to have been called.

@huyph Anda harus mengejek metode doSmth dan metode pengujian Anda agar lelucon menguji apakah itu dipanggil. Jika Anda dapat memberikan cuplikan kode ejekan Anda, saya dapat memeriksa apa yang salah

@ainesophaur ...uhm. Saya pikir kode Anda di atas adalah untuk mengejek metode test() ? bagian ini: test: jest.fn(() => {console.log('I didnt call the original')}),

@ainesophaur Saya mencoba kode Anda juga. Tapi itu tidak berhasil untuk saya. Itu tidak pernah menjalankan fungsi tiruan. Jadi, harapan itu tidak pernah terpenuhi.

Saya pikir ini melekat pada cara kerja yang membutuhkan seperti yang disebutkan di atas ... Saya berharap ada solusi untuk ini.

@cpojer Apakah ada sesuatu yang baru tentang modul yang sebagian mengejek?

@rantonmattei & @huyph Saya harus melihat cuplikan definisi tiruan Anda dan tes yang Anda jalankan. Anda harus mendefinisikan tiruan Anda sebelum file implementasi aktual diperlukan/diimpor. Sudah lama sejak saya bekerja dengan JEST, tetapi saya ingat akhirnya saya membuatnya mengejek semua yang saya butuhkan, apakah itu perpustakaan node_modules atau file di aplikasi saya. Saya agak kekurangan waktu ATM, tetapi berikut adalah beberapa tes dari proyek yang saya kerjakan menggunakan Jest.

Mengejek file dari ketergantungan

Definisi fungsi aktual dalam contoh ini dilakukan oleh react-native.. Saya mengejek file "react-native/Libraries/Utilities/dismissKeyboard.js"

Ini adalah file tiruan di bawah __mocks__/react-native/Libraries/Utilities/dismissKeyboard.js

function storeMockFunctions() {
  return jest.fn().mockImplementation(() => true);
}
jest.mock('react-native/Libraries/Utilities/dismissKeyboard', () => storeMockFunctions(), { virtual: true });
const dismissKeyboard = require('react-native/Libraries/Utilities/dismissKeyboard');
exports = module.exports = storeMockFunctions;

Saya tidak dapat menemukan file tes yang saya gunakan untuk hal di atas, tetapi itu seperti membutuhkan modul, bercanda akan menemukannya di __mocks__ dan kemudian saya bisa melakukan sesuatu seperti

expect(dismissKeyboard.mock.calls).toHaveLength(1);

Mengejek file yang Anda kendalikan
Definisi fungsi sebenarnya

export const setMerchantStores = (stores) => storage.set('stores', stores);

File uji dengan tiruan

const { storeListEpic, offerListEpic } = require('../merchant');

function storeMockFunctions() {
  const original = require.requireActual('../../common/Storage');
  return {
    ...original,
    setMerchantStores: jest.fn((...args) => original.setMerchantStores(...args)),
    setMerchantOffers: jest.fn((...args) => original.setMerchantOffers(...args)),
  };
}
jest.mock('../../common/Storage', () => storeMockFunctions());
import * as storage from '../../common/Storage';

afterEach(() => {
  storage.setMerchantStores.mockClear();
});

it('handle storeListEpic type STORE_LIST_REQUEST -> STORE_LIST_SUCCESS', async () => {
  const scope = nock('http://url')
  .get('/api/merchant/me/stores')
  .reply(200, storeData);
  const result = await storeListEpic(ActionsObservable.of(listStores())).toPromise();
  expect(storage.setMerchantStores.mock.calls).toHaveLength(1);
  expect(await storage.getMerchantStores()).toEqual({ ids: storesApiData.result, list: storesApiData.entities.store});
});

terima kasih telah berbagi @ainesophaur. Saya masih tidak bisa membuatnya bekerja dengan lelucon 18.1. Berikut adalah kode saya:

it('should save session correctly', () => {

  function mockFunctions() {
    const original = require.requireActual('./session');
    return {
      ...original,
      restartCheckExpiryDateTimeout: jest.fn((() => {
        console.log('I didn\'t call the original');
      })),
    }
  }

  jest.mock('./session', () => mockFunctions());
  const mockSession = require('./session');

  // NOTE: saveSession() will call the original restartCheckExpiryDateTimeout() instead of my
  // mock one. However, mockSession.restartCheckExpiryDateTimeout() does call the mock one
  mockSession.saveSession('', getTomorrowDate(), 'AUTH');

  // mockSession.restartCheckExpiryDateTimeout(); // does print out "I didn't call the original"

  expect(mockSession.restartCheckExpiryDateTimeout).toHaveBeenCalled();
});

Di session.js

export function saveSession(sessionId, sessionExpiryDate, authToken) {
  ....
  restartCheckExpiryDateTimeout(sessionExpiryDate);
  ...
}
....

export function restartCheckExpiryDateTimeout(...) {
....
}

Saya tidak dapat menemukan cara untuk menyelesaikan masalah ini. Bisakah saya membuka kembali ini? @cpojer

@huyph cara Anda melakukan ekspor saveSession akan memanggil restartCheckExpiryDateTimeout ditentukan secara lokal alih-alih melalui modul dan memanggil module.restartCheckExpiryDateTimeout -- dengan demikian module.restartCheckExpiryDateTimeout Anda diejek saveSession karena saveSession memanggil fungsi restartCheckExpiryDateTimeout sebenarnya telah ditentukan.

Saya akan menetapkan saveSession ke const dan kemudian melakukan saveSession.restartCheckExpiryDateTimeout = () => {...logic} . .kemudian dari dalam saveSession.saveSession , panggil saveSession.restartCheckExpiryDateTimeout alih-alih hanya restartCheckExpiryDateTimeout . Ekspor const baru Anda alih-alih fungsi aktual saveSession yang kemudian mendefinisikan metode Anda. Kemudian ketika Anda memanggil someVar.saveSession() itu akan memanggil saveSession.restartCheckExpiryDateTimeout() internal yang sekarang diejek.

Saya seharusnya menambahkan bahwa restartCheckExpiryDateTimeout() adalah fungsi yang diekspor. Bukan fungsi yang ditentukan secara lokal dalam saveSession() ... (Memperbarui komentar saya di atas). Dalam hal ini, saya pikir module.saveSession() harus memanggil module.restartCheckExpiryDateTimeout() yang benar yang diejek.

Tapi saya akan memberikan apa yang Anda sarankan di atas. Memindahkan saveSession() dan restartCheckExpiryDateTimeout() ke const lain. Terima kasih

Saya mengerti itu tidak didefinisikan dalam ruang lingkup saveSession. simpanSesi adalah
memanggil metode saudara dalam lingkup induk. Saya mengalami ini berkali-kali
dan apa yang saya sarankan berhasil

Pada 8 Mei 2017 20:38, "Huy Pham" [email protected] menulis:

Saya seharusnya menambahkan bahwa restartCheckExpiryDateTimeout() adalah yang diekspor
fungsi. Bukan fungsi yang ditentukan secara lokal dalam saveSession()...

Saya akan memberikan apa yang Anda sarankan di atas. Terima kasih


Anda menerima ini karena Anda disebutkan.
Balas email ini secara langsung, lihat di GitHub
https://github.com/facebook/jest/issues/936#issuecomment-300029003 , atau bisu
benang
https://github.com/notifications/unsubscribe-auth/AEeBdsmpOOmzvcUHB3D_-Z7MChIzt10Pks5r37WYgaJpZM4IPGAH
.

Baru saja mencobanya..Saya menemukan bahwa:

Ini TIDAK berfungsi: (yaitu restartCheckExpiryDateTimeout() asli masih dipanggil)

export session = {
   saveSession: () => {
      session.restartCheckExpiryDateTimeout();
   },
   restartCheckExpiryDateTimeout: () => {},
}

Ini berfungsi: (yaitu tiruan restartCheckExpiryDateTimeout() dipanggil sebagai gantinya). Perbedaannya adalah penggunaan function() sebagai ganti bentuk panah, dan penggunaan this. sebagai ganti session.

export session = {
   saveSession: function() {
      this.restartCheckExpiryDateTimeout();
   },
   restartCheckExpiryDateTimeout: () => {},
}

Ini bisa menjadi masalah dengan lelucon transpiling kode-kode ini ....

Cobalah untuk mengekspornya sebagai objek kelas alih-alih pojo. saya percaya
transpiler mengangkat variabel secara berbeda. Kita akan sampai di tempat kerja
tes, saya berjanji .. Sudah sekitar setengah tahun sejak saya berada di proyek
yang menggunakan lelucon tapi saya ingat masalah ini dengan baik dan akhirnya saya ingat
menemukan solusi.

Pada 9 Mei 2017 12:53, "Huy Pham" [email protected] menulis:

Baru saja mencobanya..Saya menemukan bahwa:

Ini TIDAK berfungsi: (yaitu restartCheckExpiryDateTimeout() asli adalah
masih dipanggil)

sesi ekspor = {
simpanSession: () => {
session.restartCheckExpiryDateTimeout();
},
restartCheckExpiryDateTimeout: () => {},
}

Ini TIDAK berfungsi: (yaitu restartCheckExpiryDateTimeout() asli adalah
masih dipanggil)

sesi ekspor = {
simpanSession: fungsi() {
this.restartCheckExpiryDateTimeout();
},
restartCheckExpiryDateTimeout: () => {},
}

Ini bisa menjadi masalah dengan lelucon transpiling kode-kode ini ....


Anda menerima ini karena Anda disebutkan.
Balas email ini secara langsung, lihat di GitHub
https://github.com/facebook/jest/issues/936#issuecomment-300060975 , atau bisukan
benang
https://github.com/notifications/unsubscribe-auth/AEeBdrRQExycPYiGtvm7qYi5G87w6b6Oks5r3_FlgaJpZM4IPGAH
.

@sorahn masalah yang sama. es6 + babel , Bagaimana cara mengejek?
@cpojer Apakah itu berarti es6 + babel , export const function xx() {} , ekspor banyak fungsi , Jest tidak memiliki cara untuk mengejek fungsi dalam modul (file) yang disebut oleh fungsi lain dalam modul (file) yang sama? Saya mengujinya, sepertinya saya benar. Hanya untuk pola commonjs , Jest dapat mengolok-olok fungsi dengan sukses, Seperti contoh Anda.

@ainesophaur tidak berfungsi.

modul:

export const getMessage = (num: number): string => {
  return `Her name is ${genName(num)}`;
};

export function genName(num: number): string {
  return 'novaline';
}

tes:

function mockFunctions() {
  const original = require.requireActual('../moduleA');
  return {
    ...original,
    genName: jest.fn(() => 'emilie')
  }
}
jest.mock('../moduleA', () => mockFunctions());
const moduleA = require('../moduleA');

describe('mock function', () => {

  it('t-0', () => {
    expect(jest.isMockFunction(moduleA.genName)).toBeTruthy();
  })

  it('t-1', () => {

    expect(moduleA.genName(1)).toBe('emilie');
    expect(moduleA.genName).toHaveBeenCalled();
    expect(moduleA.genName.mock.calls.length).toBe(1);
    expect(moduleA.getMessage(1)).toBe('Her name is emilie');
    expect(moduleA.genName.mock.calls.length).toBe(2);

  });

});

hasil tes:

FAIL  jest-examples/__test__/mock-function-0.spec.ts
  ● mock function › t-1

    expect(received).toBe(expected)

    Expected value to be (using ===):
      "Her name is emilie"
    Received:
      "Her name is novaline"

      at Object.it (jest-examples/__test__/mock-function-0.spec.ts:22:35)
      at Promise.resolve.then.el (node_modules/p-map/index.js:42:16)

  mock function
    ✓ t-0 (1ms)
    ✕ t-1 (22ms)

Test Suites: 1 failed, 1 total
Tests:       1 failed, 1 passed, 2 total
Snapshots:   0 total
Time:        0.215s, estimated 1s

Lihat beberapa komentar terakhir saya di atas. Lebih tepatnya yang terakhir. Milikmu
metode yang diekspor memanggil metode saudara yang dicakup secara lokal vs the
metode ekspor aktual (di situlah tiruan Anda berada)

Pada tanggal 31 Mei 2017 2:00 pagi, "novaline" [email protected] menulis:

@ainesophaur https://github.com/ainesophaur tidak berfungsi.

modul:

ekspor const getMessage = (angka: nomor): string => {
kembali Her name is ${genName(num)} ;
};
fungsi ekspor genName(num: number): string {
kembali 'novalin';
}

tes:

fungsi mockFunctions() {
const asli = require.requireActual('../moduleA');
kembali {
...asli,
genName: jest.fn(() => 'emilie')
}
}jest.mock('../moduleA', () => mockFunctions());const moduleA = require('../moduleA');
deskripsikan('fungsi tiruan', () => {

itu('t-0', () => {
harapkan(jest.isMockFunction(moduleA.genName)).toBeTruthy();
})

itu('t-1', () => {

expect(moduleA.genName(1)).toBe('emilie');
expect(moduleA.genName).toHaveBeenCalled();
expect(moduleA.genName.mock.calls.length).toBe(1);
expect(moduleA.getMessage(1)).toBe('Her name is emilie');
expect(moduleA.genName.mock.calls.length).toBe(2);

});

});

hasil tes:

GAGAL lelucon-contoh/__test__/mock-function-0.spec.ts
● fungsi tiruan t-1

expect(received).toBe(expected)

Expected value to be (using ===):
  "Her name is emilie"
Received:
  "Her name is novaline"

  at Object.it (jest-examples/__test__/mock-function-0.spec.ts:22:35)
  at Promise.resolve.then.el (node_modules/p-map/index.js:42:16)

fungsi tiruan
t-0 (1 md)
t-1 (22ms)

Test Suites: 1 gagal, total 1
Tes: 1 gagal, 1 lulus, total 2
Snapshot: 0 total
Waktu: 0,215 detik, diperkirakan 1 detik


Anda menerima ini karena Anda disebutkan.
Balas email ini secara langsung, lihat di GitHub
https://github.com/facebook/jest/issues/936#issuecomment-305091749 , atau bisu
benang
https://github.com/notifications/unsubscribe-auth/AEeBdv6SafXlTtKo3DNeFWhbL6gV9l0Gks5r_QHjgaJpZM4IPGAH
.

@ainesophaur : Saya mencoba export class Session { } . Dan itu tidak bekerja untuk saya.

Satu-satunya pendekatan yang berhasil untuk saya adalah dalam komentar saya di atas: di mana sintaks function digunakan alih-alih arrow () => . Di Sini:

export const session = {
   saveSession: function() {
      this.restartCheckExpiryDateTimeout();
   },
   restartCheckExpiryDateTimeout: () => {},
}

Ini ada di Jest 20.0.3

Apa yang saya lakukan adalah membuat pembungkus const untuk fungsi, dan kemudian mengekspor pembungkus itu (seperti export const fns). Kemudian di dalam modul gunakan fns.functionName, dan kemudian saya bisa bercanda.fn() fungsi fns.functionName

Ketika kita menulis fungsi tiruan dari modul yang ditentukan pengguna yang ditulis dalam TypeScript dan ketika kita memanggil fungsi tiruan adalah fungsi asli yang tercakup dalam laporan cakupan karena kita memanggil versi tiruan dari fungsi tersebut.

Saya memiliki 2 fungsi yang awalnya diimpor dalam tes sebagai
import { getCurrentDate, getStartEndDatesForTimeFrame } from ./../_helpers/date';
Seperti yang Anda lihat getStartEndDatesForTimeFrame bergantung pada getCurrentDate . Dengan pengaturan berikut, pengujian getCurrentDate bekerja dengan baik dan menggunakan versi tiruan. Di sisi lain untuk beberapa alasan tes getStartEndDatesForTimeFrame tidak menggunakan getCurrentDate diejek tetapi implementasi asli sehingga pengujian saya gagal. Saya telah mencoba banyak pengaturan yang berbeda (seperti Date.now = jest.fn(() => "2017-11-16T20:33:09.071Z"); tetapi tidak berhasil. Ada ide?

export const getCurrentDate = () => new Date();
export const getStartEndDatesForTimeFrame = (timeFrame) => {
  ...
  const todayDate = getCurrentDate();
  ...
  switch (timeframe) {
    case TimeFrames.TODAY:
      console.log(todayDate); // this always prints the real value in tests instead of the mocked one
      start = new Date(todayDate.getFullYear(), todayDate.getMonth(), todayDate.getDate(), 0, 0, 0);
      end = new Date(
        todayDate.getFullYear(),
        todayDate.getMonth(),
        todayDate.getDate(), 23, 59, 59,
      );
      break;
  ...
  return { start: start.toISOString(), end: end.toISOString() }
};
function mockFunctions() {
  const original = require.requireActual('../../_helpers/date');
  return {
    ...original,
    getCurrentDate: jest.fn(() => '2017-11-16T20:33:09.071Z'),
  }
}
jest.mock('../../_helpers/date', () => mockFunctions());
const dateModule = require.requireMock('../../_helpers/date');

describe('getCurrentDate', () => {
  it('returns the mocked date', () => {
    expect(dateModule.getCurrentDate()).
      toBe('2017-11-16T20:33:09.071Z'); // this works well and returns the mocked value
  });
});

describe('getStartEndDatesForTimeFrame', () => {
  it('returns the start and end dates for today', () => {
    expect(dateModule.getStartEndDatesForTimeFrame('today')).toEqual(
      { 'start': '2017-11-15T23:00:00.000Z', 'end': '2017-11-16T22:59:59.000Z' }
    ); // this one uses the original getCurrentDate instead of the mocked one :(
  });
});

Jadi getStartEndDatesForTimeFrame gagal karena menggunakan waktu saat ini bukan yang diejek.

Saya telah berhasil membuatnya bekerja dengan mengikuti saran oleh @ainesophaur - dengan mengekspor semua fungsi dalam suatu objek dan memanggil metode objek yang diekspor ini alih-alih metode saudara lingkup lokal:

// imageModel.js
const model = {
  checkIfImageExists,
  getImageUrl,
  generateImagePreview
}
export default model

async function checkIfImageExists(...) {}
async function getImageUrl() {}
async function generateImagePreview() {
  // I am calling it as `model` object's method here, not as a locally scoped function
  return model.getImageUrl(...)
}

// imageModel.test.js
import imageModel from './imageModel'

test('should mock getImageUrl called within the same file', async () => {
  imageModel.getImageUrl = jest.fn().mockReturnValueOnce(Promise.resolve())

  await imageModel.generateImagePreview()

  expect(imageModel.getImageUrl).toBeCalled()
})

@miluoshi Itulah satu-satunya cara saya bisa melakukannya juga. Apakah ada penurunan performa atau semacamnya ketika kita menggunakan metode ini? Tampaknya "salah" untuk mengubah kode sehingga Anda dapat mengujinya.

Saya sangat ingin cara untuk menulis:
jest.mock('src/folder/file.func, () => {return 'whatever i want'})

bagian kunci di sini adalah .func

@miluoshi @Rdlenke jika kode Anda terdiri dari ekspor bernama, Anda juga dapat import * as model dan kemudian menimpa model.generateImagePreview = jest.fn(() => Promise.resolve);

Bagaimana Anda mengujinya dengan sinon? Seperti yang disebutkan sebelumnya (lihat https://github.com/facebook/jest/issues/936#issuecomment-214939935), cara kerja ESM tidak memungkinkan untuk mengejek func2 dalam func1 , jadi Saya tidak akan menyebutnya dasar.

Mungkin mod babel dapat ditulis yang berbunyi di fungsi "testImport" apa pun
dan menulis ulang kode untuk mengekspor fungsi dalam modul sebelum
tes berjalan?

Pada Senin, 18 Desember 2017 pukul 17.00, Jim Moody [email protected] menulis:

Anda benar @SimenB https://github.com/simenb , saya telah mengubah sesuatu
dalam pengujian saya di antara beralih ke Sinon yang sepertinya sudah lulus.
Ketika saya mengembalikannya, itu masih tidak berfungsi. Saya kira itu tidak masalah
yang telah dipecahkan.


Anda menerima ini karena Anda disebutkan.
Balas email ini secara langsung, lihat di GitHub
https://github.com/facebook/jest/issues/936#issuecomment-352488400 , atau bisu
benang
https://github.com/notifications/unsubscribe-auth/AQRY9a5-s2_bjCWKNw5WiAJW-JeBf8W3ks5tBpoygaJpZM4IPGAH
.

--

Darren Cresswell
Pengembang Kontrak | Pengembang Terbatas
Email: [email protected]
Telepon:
Situs web: http://www.develer.co.uk

Silakan mempertimbangkan lingkungan sebelum mencetak e-mail
PERINGATAN: Virus komputer dapat ditularkan melalui email. Penerima
harus memeriksa email ini dan lampiran apa pun untuk mengetahui keberadaan virus.
Developer Limited tidak bertanggung jawab atas kerusakan apa pun yang disebabkan oleh virus apa pun
dikirimkan melalui email ini. Transmisi email tidak dapat dijamin
aman atau bebas dari kesalahan karena informasi dapat disadap, rusak, hilang,
hancur, datang terlambat atau tidak lengkap, atau mengandung virus. Pengirim
oleh karena itu tidak bertanggung jawab atas kesalahan atau kelalaian apa pun dalam
isi pesan ini, yang muncul sebagai akibat dari transmisi email.

PERINGATAN: Meskipun Developer Limited telah mengambil tindakan pencegahan yang wajar untuk
pastikan tidak ada virus dalam email ini, perusahaan tidak dapat menerima
tanggung jawab atas kehilangan atau kerusakan yang timbul dari penggunaan email ini atau
lampiran.

Developer Limited adalah perusahaan terbatas yang terdaftar di Inggris dan Wales. |
No Pendaftaran Perusahaan 09817616 | Kantor Terdaftar: SUITE 1 SECOND
RUMAH LANTAI EVERDENE, JALAN DEANSLEIGH, BOURNEMOUTH, INGGRIS, BH7 7DU

terima kasih @ainesophaur atas solusinya.

Jika ada yang menemukan contoh kerja non async berguna, ini milik saya:

//reportError.js
const functions = {};

functions._reportToServer = (error, auxData) => {
  // do some stuff
};

functions.report = (error, auxData = {}) => {
  if (shouldReportToServer()) {
    functions._reportToServer(error, auxData);
  }
};
export default functions;

// reportError.jest.js
import reportError from 'app/common/redux/lib/reportError';
reportError._reportToServer = jest.fn();

describe('test reportError', () => {
  it('reports ERROR to server as as error', () => {
   reportError.report(new Error('fml'), {});
    expect(reportError._reportToServer).toHaveBeenCalledTimes(1);
  });
});

@jim-moody Jika saya memahami masalahnya dengan benar, ini akan berfungsi untuk contoh Anda:

const spyOnExampleFunc2 = jest.spyOn(example, 'func2');
example.func1();
expect(spyOnExampleFunc2).toBeCalled();

(_only_ ini berfungsi jika fungsinya diekspor sebagai const, seperti pada contoh Anda)

@dinvlad pahlawanku!

Mengikuti dari jawaban @dinvlad , saya pikir menambahkan, menunjukkan dengan contoh atau menautkan dokumen terkait tiruan berikut pada halaman objek lelucon ke halaman fungsi Mock mungkin merupakan peningkatan pada dokumen lelucon tentang mengejek:

  • jest.isMockFunction(fn)
  • jest.genMockFromModule(namamodul)
  • jest.mock(namamodul, pabrik, opsi)
  • jest.unmock(namamodul)
  • jest.doMock(namamodul, pabrik, opsi)
  • jest.dontMock(namamodul)

Kasus penggunaan saya adalah sebagai pengguna baru lelucon saya memigrasikan beberapa kode moka + sinon.js ke lelucon. Saya sudah memiliki mata-mata dan harapan jadi saya pikir itu akan mudah. Tetapi setelah membaca utas ini dan membaca dokumen lelucon tentang fungsi Mock, saya mendapat kesan bahwa menggunakan lelucon dengan cara ini mungkin memerlukan penulisan ulang tes saya atau pemahaman terperinci tentang ESM atau Babel ... atau kebingungan lainnya.

Terima kasih untuk Jest - ini membuat pengujian saya lebih mudah untuk ditulis/dipahami dan lebih cepat untuk dieksekusi. :)

PR yang mengklarifikasi dokumen sangat disambut! 🙂

Untuk mengejek hanya modul tertentu dengan sintaks modul ES, Anda dapat menggunakan require.requireActual untuk memulihkan modul asli, lalu menimpa modul yang ingin Anda tiru:

import { foo } from './example';

jest.mock('./example', () => (
  ...require.requireActual('./example'),
  foo: jest.fn()
));

test('foo should be a mock function', () => {
  expect(foo('mocked!')).toHaveBeenCalledWith('mocked!');
});

Terasa terbalik, tapi itu cara paling sederhana yang pernah saya temui. Kiat topi untuk @joshjg.

Saya tersesat di suatu tempat dalam diskusi panjang, saya hanya punya pertanyaan, apakah ada cara untuk menguji apakah implementasi fungsi yang sebenarnya dipanggil?

Dari apa yang saya pahami, jika saya perlu menggunakan jest.fn() itu akan menimpa fungsi aslinya, tetapi jika saya tidak menggunakannya, konsol akan memberi saya kesalahan dengan mengatakan itu harus jest.fn() function or a spy

Saya mencoba menguji middleware di mana permintaan akan diteruskan, jadi jika saya mengejeknya, semua logika akan hilang dan data tidak akan diteruskan ke middleware berikutnya. Jika saya tidak mengejeknya, dengan mengimpornya, apakah saya dapat menguji fungsi ini telah dipanggil?

Anda dapat menggunakan jest.spyOn , mungkin? Secara default ia memanggil fungsi yang mendasarinya

Terima kasih atas bantuannya, saya mencoba, tetapi tes menunjukkan bahwa itu tidak pernah dipanggil meskipun dipanggil karena saya meletakkan console.log dan itu mencetak

berkas tes

import errorHandler from '../../controller/errorHandler'

describe('auth test', () => {
  describe('test error: ', () => {
    const test1 = jest.spyOn(errorHandler, 'handleClientError')
    test('should return 400', (done) => {
      request(app)
      .post('/auth/error')
      .then((res) => {
        expect(res.statusCode).toBe(400)
        expect(test1).toBeCalled()
        done()
      })
    })

penangan kesalahan

module.exports = {
  handleClientError () {
    console.log('err')
  }
}

menghibur

console.log src/controller/errorHandler.js:10
      err

  ● auth test › test error:  ›  should return 400

    expect(jest.fn()).toBeCalled()

    Expected mock function to have been called.

      18 |         expect(res.statusCode).toBe(400)
    > 19 |         expect(test1).toBeCalled()
      20 |         done()
      21 |       })
      22 |     })

Apakah fungsinya disebut handleClientError atau logError ?

@WangHansen Dari contoh Anda, kode Anda seharusnya expect(errorHandler.handleClientError).toBeCalled() // > true

@WangHansen dapatkah Anda menambahkan .mockImplementation() ke jest.spyOn() ? Sebagai seseorang yang berasal dari Jasmine, saya menemukan tip ini penting untuk mencapai fungsi yang sama dengan mata-mata Jasmine. Misalnya

const mockModuleFunction = jest
  .spyOn(module, 'function')
  .mockImplementation(() => 'hello');
...
expect(mockModuleFunction.mock).toBeCalled();

Jika Anda _tidak_ menggunakan mockImplementation() , maka jest.spyOn() menghasilkan objek yang _bukan_ tiruan (afaiu) dan sebenarnya tunduk pada implementasi asli. Jika Anda memang harus mempertahankan implementasi asli, mungkin itu layak digunakan

const moduleFunction = module.function;
jest.spyOn(module, 'function').mockImplementation(moduleFunction);
...

Tidak yakin ini perlu, tetapi cukup yakin itu _harus_ bekerja.

Kembali ke permintaan awal...

Tidak bisakah Anda membungkus proxy di sekitar impor *? misalnya

import * sebagai tes dari './myfile.js';

pengendali const = {
/** Intercepts: mendapatkan properti */
get(target, propKey, penerima) {
console.log( GET ${propKey} );
kembali 123;
},

/** Intercepts: checking whether properties exist */
has(target, propKey) {
    console.log(`HAS ${propKey}`);
    return true;
}};

const p = Proxy baru(tes);

Pada Selasa, 30 Januari 2018 pukul 16:24, Denis Loginov [email protected]
menulis:

@WangHansen https://github.com/wanghansen dapatkah Anda menambahkan
.mockImplementation() ke jest.spyOn() Anda? Sebagai seseorang yang datang dari
Jasmine, saya menemukan tip ini penting untuk mencapai fungsi yang sama seperti
mata-mata Jasmine. Misalnya

const mockModuleFunction = lelucon
.spyOn(modul, 'fungsi')
.mockImplementation(() => 'halo');...expect(mockModuleFunction.mock).toBeCalled();

Jika Anda tidak menggunakan mockImplementation(), maka jest.spyOn() menghasilkan sebuah
objek yang bukan tiruan (afaiu) dan sebenarnya tunduk pada aslinya
penerapan. Jika Anda harus mempertahankan implementasi asli, mungkin itu
layak digunakan

const moduleFunction = module.function;jest.spyOn(module, 'function').mockImplementation(moduleFunction);...

Tidak yakin ini perlu, tetapi cukup yakin itu harus berhasil.


Anda menerima ini karena Anda disebutkan.
Balas email ini secara langsung, lihat di GitHub
https://github.com/facebook/jest/issues/936#issuecomment-361648414 , atau bisukan
benang
https://github.com/notifications/unsubscribe-auth/AQRY9VXyHNYAtwOOY6EV637WGQH9k5Plks5tP0I9gaJpZM4IPGAH
.

--

Darren Cresswell
Pengembang Kontrak | Pengembang Terbatas
Email: [email protected]
Telepon:
Situs web: http://www.develer.co.uk

Silakan mempertimbangkan lingkungan sebelum mencetak e-mail
PERINGATAN: Virus komputer dapat ditularkan melalui email. Penerima
harus memeriksa email ini dan lampiran apa pun untuk mengetahui keberadaan virus.
Developer Limited tidak bertanggung jawab atas kerusakan apa pun yang disebabkan oleh virus apa pun
dikirimkan melalui email ini. Transmisi email tidak dapat dijamin
aman atau bebas dari kesalahan karena informasi dapat disadap, rusak, hilang,
hancur, datang terlambat atau tidak lengkap, atau mengandung virus. Pengirim
oleh karena itu tidak bertanggung jawab atas kesalahan atau kelalaian apa pun dalam
isi pesan ini, yang muncul sebagai akibat dari transmisi email.

PERINGATAN: Meskipun Developer Limited telah mengambil tindakan pencegahan yang wajar untuk
pastikan tidak ada virus dalam email ini, perusahaan tidak dapat menerima
tanggung jawab atas kehilangan atau kerusakan yang timbul dari penggunaan email ini atau
lampiran.

Developer Limited adalah perusahaan terbatas yang terdaftar di Inggris dan Wales. |
No Pendaftaran Perusahaan 09817616 | Kantor Terdaftar: SUITE 1 SECOND
RUMAH LANTAI EVERDENE, JALAN DEANSLEIGH, BOURNEMOUTH, INGGRIS, BH7 7DU

@dinvlad @iampeterbanjo @SimenB Sekali lagi terima kasih atas semua bantuan Anda, tetapi sayangnya, tidak ada cara yang Anda sarankan berhasil. Saya ingin tahu apakah itu karena fungsi tersebut dipanggil dalam bentuk next(err) . Logikanya adalah, ketika permintaan gagal, permintaan akan diteruskan ke errorHandler dengan memanggil return next(err) . Pasti fungsinya dipanggil karena ketika saya menambahkan console.log , itu sedang dicetak. Tetapi tes menunjukkan itu tidak pernah disebut

@dinvlad Saya mencoba metode Anda, itu tidak berhasil, tetapi terima kasih atas bantuan Anda. Saya hanya ingin tahu mengapa Anda perlu memanggil mockImplementation , menurut dokumen resmi di jest.spyOn , Anda hanya memanggilnya ketika Anda ingin mengganti fungsi aslinya.

@WangHansen ya Anda benar bahwa itu hanya diperlukan ketika seseorang ingin mengganti metode aslinya. Saya hanya melemparkan ide untuk situasi itu.

Salah satu alasan mengapa itu mungkin gagal untuk Anda adalah asinkronisitas. Jika metode Anda menggunakan panggilan balik dan/atau Janji (atau async/menunggu), maka Anda perlu memastikan harapan Anda benar-benar dijalankan sebelum metode pengujian Anda berakhir. Ada metode khusus expect.assertions(N) untuk menegaskan itu. Juga pastikan bahwa harapan Anda dijalankan hanya setelah kode di dalam panggilan balik/janji telah dipanggil. Saya yakin Anda telah melihatnya tetapi hanya untuk referensi, https://facebook.github.io/jest/docs/en/asynchronous.html

Sangat disayangkan bahwa mengejek fns yang digunakan secara internal seperti yang dijelaskan oleh @seibelj tidak dimungkinkan tanpa mengubah impl modul.

Menurut pendapat saya, tes seharusnya tidak mendorong bagaimana logika bisnis diimplementasikan (setidaknya tidak sampai tingkat ini)

Apakah ada rencana untuk mengimplementasikan perilaku seperti itu dengan bercanda?

Hai,
Agak terlambat untuk seluruh diskusi, tetapi membaca seluruh diskusi - saya masih belum berhasil melakukannya.
Solusi menjanjikan
Apakah ada yang berubah sejak awal diskusi ini? Apakah saya melewatkan sesuatu?

Saya mengadaptasi solusi @greypants sedikit dan itu berhasil untuk saya. Inilah kode saya jika itu dapat membantu seseorang:

import Module from './module'
import { getJSON } from './helpers'

jest.mock('./helpers', () =>
  Object.assign(require.requireActual('./helpers'), {
    getJSON: jest.fn()
  })
)

test('should use the mock', async () => {
  getJSON.mockResolvedValue('foo')
  await expect(module().someAsyncThingThatUsesGetJSON()).resolves.toEqual('foo')
})

test('should use the actual module', () => {
  expect(module().someFunctionThatUsesAHelper()).toBe(true)
})

Ini masih terasa agak kacau dan dokumennya tidak terlalu membantu. Saya berterima kasih untuk Jest dan tim di belakangnya tetapi sepertinya kasus penggunaan umum yang setidaknya harus memiliki solusi "resmi".

@sarahdayan , dan siapa pun yang mungkin tertarik -
Saya akhirnya menggunakan babel-plugin-rewire.
Butuh beberapa saat bagi saya untuk menemukan plugin itu, tetapi itu adalah solusi yang cukup koheren untuk tidak merasa hacky.

Dalam kebanyakan kasus, kita ingin tidak mengejek satu atau lebih fungsi dari modul. Jika Anda menggunakan sistem mengejek lelucon global, Anda dapat mencapainya menggunakan genMockFromModule dan requireActual . Berikut adalah contohnya:

//  __mocks__/myModule.js
const moduleMock = jest.genMockFromModule('./../myModule');

// in most cases we don't want to mock `someFunction`
moduleMock.someFunction = jest.fn(
  (...args) => require.requireActual('./../myModule').someFunction(...args)
)

module.exports = moduleMock;

Solusi ini memungkinkan menggunakan tiruan untuk fungsi lain dari modul, menggunakan implementasi asli someFunction ketika seluruh modul diejek dan juga memungkinkan untuk mengejek fungsi someFunction dengan mockImplementationOnce atau mockImplementation API.

Saya telah membaca semua percakapan di atas, tetapi tidak ada solusi yang berhasil untuk saya.
Jika Anda masih mencari solusi untuk kasus uji ini, jawabannya adalah babel-plugin-rewire , plugin ini untuk menyelesaikan kasus skenario yang telah kita bahas.
Silakan lihat di lib itu, Anda akan berterima kasih kepada saya kembali nanti.

Jadi untuk meringkas seluruh utas di atas:

  1. Misalkan Anda memiliki modul m dengan fungsi f , g dan h mana g dan h memanggil f . Kami ingin membuat tiruan f sehingga g dan h memanggil tiruan alih-alih f . Sayangnya ini tidak mungkin dilakukan secara langsung kecuali f selalu dipanggil melalui exports seperti yang dijelaskan cpojer . Ini tidak mungkin jika modul Anda menggunakan sintaks impor/ekspor ES6 di TypeScript (dan saya kira hal yang sama berlaku di Babel).
  2. Namun, misalkan kita memindahkan f ke modul lain m2 . Kemudian m akan memiliki pernyataan seperti import {f} from 'm2' dan ketika g dan h call f , mereka sebenarnya memanggil m2.f where m2 = require('./m2') (begitulah terjemahan Babel/TypeScript akan terlihat). Ini memungkinkan untuk mengejek f andal seperti yang dijelaskan oleh greypants . Dengan kata lain, Anda dapat mengejek panggilan dengan andal hanya jika panggilan tersebut melewati batas modul . Catatan: solusi greypants sekarang menghasilkan pesan kesalahan ini: "Pabrik modul jest.mock() tidak diizinkan untuk mereferensikan variabel di luar cakupan - Akses variabel tidak valid: __assign". Saya menduga ini adalah bug di Jest; sebagai solusinya, gunakan Object.assign seperti yang ditunjukkan di bawah ini.
  3. Tetapi jika, alih-alih mengejek satu atau dua fungsi, Anda ingin mengejek semuanya kecuali satu atau dua fungsi, gunakan kode seperti darkowic's .

Contoh (2):

~~~js
// modul m.js
impor {f} dari './m2'
fungsi ekspor g() { kembali 'f dikembalikan ' + f(); };

// modul m2.js
fungsi ekspor f() { mengembalikan 'f yang sebenarnya'; }

// tes.js
impor * sebagai m dari './m'

jest.mock('./m2', () => Object.assign(
membutuhkan.requireActual('./m2'), {
f: jest.fn().mockReturnValue('MOCK')
}));

tes('diolok-olok', () => {
harapkan(mg()).toEqual('f mengembalikan MOCK');
});
~~~

Saat menguji ini, saya mengalami #2649: memanggil jest.mock di dalam tes tidak berpengaruh, dan jika Anda menyebutnya di lingkup global maka Anda tidak dapat unmock sebelum tes lain. Sangat menyebalkan.

Terima kasih!! @sarahdayan
Udah lama cari ini

Jika dokumennya kurang, PR selalu dipersilakan untuk mengklarifikasinya

Halo semuanya!

Saya bermain-main sedikit dan memiliki ide berikut untuk menyelesaikan masalah itu:

  • mock modul, tetapi modul mocked memiliki modul asli dalam rantai prototipe.
  • menyediakan metode untuk menambahkan properti ke modul tiruan (yang akan menimpa properti dari prototipe)
  • juga menyediakan metode untuk menghapus properti dari modul tiruan (untuk menggunakan properti dari prototipe lagi).
// m1.js
export const f = () => "original f"

// __mocks__/m1.js
const originalM1 = require.requireActual("../m1");
// set the original as a prototype
let mockModule: any = Object.create(originalM1);
const __setMock = (name, value) => {
  mockModule[name] = value;
};
const __removeMock = (name) => {
  Reflect.deleteProperty(mockModule, name);
};
// enhance the mocked module to allow overriding original exports
module.exports = Object.assign(mockModule, { __setMock, __removeMock });


// m1.test.js
import { f } from "./m1";

jest.mock("./m1");

test("mocking stuff", () => {
  // here nothing is mocked - the original module is used
  expect(f()).toBe("original f");

  // override the export f of the module
  require("./m1").__setMock("f", () => "mocked f");
  expect(f()).toBe("mocked f");

  // set it back to the original
  require("./m1").__removeMock("f");
  expect(f()).toBe("original f");

  //override with another value
  require("./m1").__setMock("f", () => "another mocked f");
  expect(f()).toBe("another mocked f");
});

2 sen saya:

Saya menguji banyak solusi (jika tidak semuanya) dan satu-satunya yang berhasil untuk saya adalah yang ini (bercanda 23):

// importedModule.js
export const funcB = () => {
  return "not mocked";
};
export const funcA = () => {
  return mockProxy.funcB(); // this works but it's soooo hacky
};

export const mockProxy = {
  funcB
};

// moduleToTest.js
import { funcA } from "./moduleImported.js";

export default function() {
  return funcA();
}

// test
let moduleImported = require("./moduleImported.js");
moduleImported.mockProxy.funcB = jest.fn(() => "mocked");
const funcToTest = require("./moduleToTest.js").default; // or import

it("should return mocked", function() {
  expect(funcToTest()).toBe("mocked");
});

Saya sampai pada kesimpulan bahwa bukan ide yang baik untuk mencoba melakukan ini karena:

  • test.js tahu terlalu banyak tentang detail implementasi importedModule.js
  • solusinya adalah rapuh dan tidak ada yang akan memahami tujuan mockProxy dengan melihat importedModule.js

Adakah yang menemukan solusi untuk ini yang berfungsi?

Saya menggunakan:

"jest": "^21.2.1",
"jest-cli": "^21.2.1",

@jamesone sudahkah Anda membaca ini https://github.com/facebook/jest/issues/936#issuecomment -410080252 ?

Ini bekerja untuk saya, berdasarkan jawaban oleh @thomaskempel :

Dalam kasus saya, saya ingin mengejek ketergantungan pada node_modules, sebut saja 'komponen bersama'. Ini mengekspor sejumlah komponen bernama. Saya ingin mengejek hanya beberapa dari ekspor bernama ini, dan membiarkan sisanya sebagai hal yang nyata.

Jadi di __mocks__/shared-components.js saya punya:

const original = require.requireActual('shared-components');

module.exports = {
...original,
moduleNameToOverride: jest.fn().mockImplementation(() => {
      return 'whatever';
    }),
}

Dalam kasus saya, saya mematikan implementasinya. Semoga ini bisa membantu seseorang di masa depan.

Saya menghadapi masalah yang sama baru-baru ini, percakapan di utas ini membantu saya untuk lebih memahami dan saya merangkum temuan saya di sini https://medium.com/@DavideRama/mock -spy-exported-functions-within-a-single- module-in-jest-cdf2b61af642

Terinspirasi oleh solusi @qwertie

mockGet = jest.fn()
jest.mock('my-module', () => ({
  ...jest.requireActual('my-module'),
  get: mockGet
}))

@MajorBreakfast yang bekerja dengan React.lazy ?

const mockLazy = jest.fn();

jest.mock('React', () => ({
    ...jest.requireActual('React'),
    lazy: mockLazy
}));

Saya masih mendapatkan ReferenceError: React is not defined .

Untuk mengejek modul fungsi ekspor independen:
Ekspor semua bagian fungsi individual dari ekspor default dari file.

Contoh:
dataDao.js

fungsi getData()
fungsi setData()
fungsi hapusData()
ekspor {getData, setData, deleteData}

Sekarang Anda dapat mengimpor semua fungsi dari file ke tes lelucon Anda dengan penamaan default;

dataDao.spec.js

impor * sebagai dataDao dari '../dataDao';
// Memata-matai modul yang mereferensikan nama default yang ditetapkan saat impor
jest.spyOn(dataDao, 'getData')
jest.spyOn(dataDao, 'setData')
jest.spyOn(dataDao, 'deleteData')

@vchinthakunta , itu mungkin berhasil, tetapi sepertinya pelanggaran tujuan utama sintaks ekspor/impor: modul lain tidak akan lagi dapat mengimpor metode atau bidang data tertentu melalui

import { justThisThing } from 'someModule';

Apakah saya melewatkan sesuatu di sana?

@MajorBreakfast yang bekerja dengan React.lazy ?

const mockLazy = jest.fn();

jest.mock('React', () => ({
    ...jest.requireActual('React'),
    lazy: mockLazy
}));

Saya masih mendapatkan ReferenceError: React is not defined .

Saya pikir 'Bereaksi' harus huruf kecil di sini karena merujuk pada impor?

jest.mock('react'...)

Saya membuat kode saya berfungsi menggunakan yang berikut ini yang lebih sederhana daripada solusi lain yang pernah saya lihat di sini. Anda tidak perlu menggunakan require atau mengatur ekspor default .

pembantu/navigasi.js

export const initHeader = () => {
    // initialise header
    ...
}

...

export const anotherHelperFunction = () => {
    // do stuff
    ...
}

komponen yang menggunakan navigation.js

import { initHeader } from '../helpers/navigation';

jest.mock('../helpers/navigation');

...

describe('Test component', () => {

    it('should reinitialise header', () => {
        const mockHeaderInit = jest.fn();
        initHeader.mockImplementation(mockHeaderInit);

        const component = mountComponent(mockProps);
        component.simulate('click');

        expect(mockHeaderInit).toBeCalled();
    }
}
mockGet = jest.fn()
jest.mock('my-module', () => ({
  ...jest.requireActual('my-module'),
  get: mockGet
}))
ReferenceError: mockGet is not defined

       4 | const mockGet = jest.fn();
       5 | jest.mock('./browserStorage', () => ({
       6 |   ...jest.requireActual('./browserStorage'),
    >  7 |   get: mockGet,
         |        ^
       8 | }));

jest.mock diangkat, gunakan doMock atau

jest.mock('./browserStorage', () => ({
  ...jest.requireActual('./browserStorage'),
  get: jest.fn(),
}));

const {get: mockGet} = require('./browserStorage');

Ini sepertinya masalah yang cukup umum. Apakah ada sesuatu tentang ini di dokumen lelucon?

Jadi, apakah ada solusi untuk mengejek fungsi dalam modul yang sama ?

Metode berikut berhasil untuk saya, triknya adalah mengatur ulang fungsi yang diejek kembali di akhir tes.
Contoh ini mengolok-olok fungsi verifikasi dari modul jsonwebtoken.

  test('perform my test', async () => {
    // save the real jwt.verify function
    const verify = jwt.verify
    // mock it.
    jwt.verify = jest.fn().mockReturnValue({ sub: 0 })
    // do the test
    ...
    // set the real function back.
    jwt.verify = verify
  })

Metode berikut berhasil untuk saya, triknya adalah mengatur ulang fungsi yang diejek kembali di akhir tes.
Contoh ini mengolok-olok fungsi verifikasi dari modul jsonwebtoken.

  test('perform my test', async () => {
    // save the real jwt.verify function
    const verify = jwt.verify
    // mock it.
    jwt.verify = jest.fn().mockReturnValue({ sub: 0 })
    // do the test
    ...
    // set the real function back.
    jwt.verify = verify
  })

di mana Anda menggunakan const verify ? lagi pula, ini hanya akan berfungsi jika fungsi tiruan Anda bukan fungsi const yang diekspor

Jadi, apakah ada solusi untuk mengejek fungsi _dalam modul yang sama_?

Setelah banyak mencari, solusi untuk masalah ini adalah menyimpan ekspor Anda dalam satu objek yang dapat dirujuk oleh fungsi dan tiruan Anda. Semua artikel di bawah ini mencapai konsensus yang sama.

https://github.com/facebook/jest/issues/936#issuecomment -438975674
https://medium.com/@qjli/how -to-mock-specific-module-function-in-jest-715e39a391f4
https://luetkemj.github.io/170421/mocking-modules-in-jest

Sangat mengejutkan bagi saya bahwa tidak ada yang menyebutkan solusi berbeda yang, jika dapat diterapkan untuk kode Anda, sepenuhnya menghilangkan semua masalah ini dan membuat pengujian kode yang diinginkan menjadi sangat, sangat sederhana:

_Pindahkan satu fungsi yang ingin Anda tiru ke dalam modulnya sendiri._

Dengan serius. Jika modul Anda ditulis sedemikian rupa sehingga Anda perlu menguji bagian dalamnya secara terpisah dari bagian dalam lainnya, maka hampir pasti kelas Anda melanggar Prinsip Tanggung Jawab Tunggal (ya, ini bukan kelas nyata, tetapi modul berfungsi seperti kelas, modul menjadi wadah unit kode). Pisahkan pengisap itu, dan boom, Anda bisa mengejek kebutuhannya.

Jika fungsi yang diejek bergantung pada sekelompok status pribadi, ini masih bukan alasan yang baik untuk tidak membagi modul Anda entah bagaimana. Fakta bahwa itu bergantung pada sekelompok keadaan internal menyiratkan, bagi saya, bahwa masalah modul tidak dipikirkan dengan jelas. Mungkin bahkan ada modul ketiga untuk dipisah, yang mewakili semacam kelas data atau DTO, yang dapat diteruskan sebagai argumen.

Juga, apakah Anda mengekspor fungsi hanya untuk pengujian yang seharusnya bersifat pribadi? Mengapa kode eksternal akan memanggil fungsi yang diejek secara langsung, tetapi juga memanggil fungsi lain yang perlu dipanggil sendiri? Saya yakin ada semacam pemutusan yang terjadi di sini. Mungkin fungsi yang diejek perlu tetap ada tetapi semua fungsi harus dibagi dua dengan setengahnya dihapus masuk ke modul lain. Anda mendapatkan idenya.

Saat pengujian menjadi sangat sulit, itu hampir selalu merupakan tanda refactoring diperlukan ...

Tidak diperlukan pengujian tolol:

const tomfoolery = require('tomfoolery'); // no longer required

Setelah membaca utas ini dan menguji solusi yang diusulkan, saya masih tidak bisa menjalankannya. Dari apa yang saya baca, beberapa orang membuat ini berhasil tetapi saya tidak tahu caranya.

Bisakah seseorang memberi tahu saya kode yang perlu saya tambahkan ke contoh berikut untuk membuat tes lulus?

// a.js
export const foo = () => 'foo-' + bar()
export const bar = () => 'bar'
// a.test.js
import {
  foo,
  bar
} from './a'

describe('foo', () => {
  it('should return foo-MOCKED_BAR', () => {
    expect(foo()).toBe('foo-MOCKED_BAR')
  })

  it('should have the mocked implementation of bar', () => {
    expect(bar()).toBe('MOCKED_BAR')
  })
})

describe('bar', () => {
  it('should have the original implementation of bar', () => {
    expect(bar()).toBe('bar')
  })
})

describe('foo and bar together', () => {
  it('should have the original implementation of both', () => {
    expect(foo()).toBe('foo-bar')
  })
})

Terima kasih!

Saya hanya ingin mengejek satu metode lodash seperti lodash.random dan dapat melakukannya dengan mudah dengan:

modul.js

const lodash = require('lodash');

module.exports = function() {
  return lodash.random();
}

tes.js

const lodash = require('lodash');
const module = require('./module.js);

it('mocks lodash', () => {
    jest.spyOn(lodash, 'random').mockImplementationOnce(() => {
      return 2;
    });

    expect(module()).toEqual(2)
});

Semoga membantu :)

Sesuatu yang berhasil untuk tim kami yang bekerja dengan TypeScript adalah membuat const kami ekspor alih-alih mengekspor fungsi secara langsung.
Tidak berfungsi:
export function doSomething(a, b) {}
Bekerja:
export const doSomething = function (a, b) {}

Saya mengubah ekspor seperti yang dilakukan @arbielsk , berhasil! Tapi saya tidak tahu apa perbedaan antara dua jenis ekspor ...

@dgrcode Apakah Anda pernah menemukan solusi untuk contoh Anda? Sejauh yang saya tahu, apa yang Anda coba lakukan tidak didukung dengan mengejek melalui Jest. Secara khusus, saya pikir mengejek hanya pada dasarnya adalah memasang kembali impor sehingga tampilan eksternal modul melihat metode yang diejek. Namun, dalam contoh Anda, foo dan bar berada dalam modul yang sama sehingga tampilan foo tentang bar tidak dapat diejek.

Saya percaya bahwa pilihan Anda adalah:
1) Atur ulang kode Anda sehingga foo mengimpor modul yang menyertakan bar
2) Gunakan babel-plugin-rewire

Tolong koreksi saya jika saya salah paham!

Saya memiliki persyaratan yang sedikit berbeda: Saya ingin mengejek seluruh modul _except_ untuk satu fungsi. Menggunakan solusi @MajorBreakfast sebagai titik awal, saya menemukan yang berikut:

jest.mock('my-module', () => ({
  ...jest.genMockFromModule('my-module'),
  myFunction: jest.requireActual('my-module').myFunction
}))

@dgrcode Apakah Anda pernah menemukan solusi untuk contoh Anda? Sejauh yang saya tahu, apa yang Anda coba lakukan tidak didukung dengan mengejek melalui Jest. Secara khusus, saya _berpikir_ bahwa mengejek pada dasarnya adalah memasang kembali impor sehingga tampilan eksternal modul melihat metode yang diejek. Namun, dalam contoh Anda, foo dan bar berada dalam modul yang sama sehingga tampilan foo tentang bar tidak dapat diejek.

Saya percaya bahwa pilihan Anda adalah:

  1. Atur ulang kode Anda sehingga foo mengimpor modul yang menyertakan bar
  2. Gunakan babel-plugin-rewire

Tolong koreksi saya jika saya salah paham!

Itu sebenarnya kasusku
Pemahaman saya tentang bagaimana modul bisa diejek semuanya kacau

Pada dasarnya
Jika 2 fungsi berada dalam modul yang sama, dan saling memanggil

Jika foo memanggil bar

function bar() {
  return 'some-result'
}

function foo(){
  return bar()  // <-- the function I want to mock 
}

Letakkan bar di file baru (untuk diejek)

Saya memindahkan metode bar di file baru, dan sekarang saya bisa menggunakan banyak contoh di atas
Terima kasih banyak kepada @yoni-abtech yang membuat saya mengerti itu

Cara saya melihatnya setelah membaca seluruh utas ini dan menguji berulang-ulang, ada 3 opsi ....

Opsi 1 - mendeklarasikan semua fungsi menggunakan const

Ini mengharuskan Anda untuk mengamanatkan penggunaan ekspresi fungsi di atas deklarasi . Untungnya, aturan eslint func-style mendukung Anda.

Menggunakan export const memungkinkan Anda untuk spyOn fungsi yang digunakan oleh fungsi lain dari dalam modul _same_ .

// hello.js
export const message = () => {
  return 'Hello world';
}

export const foo = () => {
  return message();
}
// hello.test.js
import * as testModule from './hello.js';

describe('test spyon with function expressions', function () {
  afterAll(() => {
    jest.restoreAllMocks();
  });
  it('should NOT mock message in foo', function () {
    const actual = testModule.foo();

    expect(actual).toBe('Hello world');
  });

  it('should mock message in foo', function () {
    jest.spyOn(testModule, 'message').mockReturnValue('my message');

    const actual = testModule.foo();

    expect(actual).toBe('my message');
    expect(testModule.message).toHaveBeenCalledTimes(1);
  });
});

Opsi 2 - Gunakan plugin babel rewire

Jika Anda tidak ingin mewajibkan penggunaan ekspresi fungsi (yaitu menggunakan const ) ini bisa menjadi pendekatan yang baik.

Ini memungkinkan Anda untuk _rewire_ (alias tiruan) fungsi dari modul yang sama. Saya bisa membayangkan kodenya akan terlihat seperti itu di bawah ini tetapi belum mengujinya. Juga dari dokumen mereka sepertinya Anda dapat memasang kembali fungsi dalam modul yang sama yang bahkan tidak diekspor dari modul 👍, bayangkan contoh di bawah ini tanpa mengekspor fungsi message .

Contoh:

// hello.js
export function message() {
  return 'Hello world';
}

export function foo() {
  return message();
}
// hello.test.js
import * as testModule from './hello.js';

describe('test rewire api', function() {
  it('should NOT mock message in foo', function () {
    const actual = testModule.foo();

    expect(actual).toBe('Hello world');
  });

  it('should mock message in foo', function () {
    testModule.__RewireAPI__.__Rewire__('message', jest.fn().mockReturnValue('my message'));

    const actual = testModule.foo();

    expect(actual).toBe('my message');
    expect(testModule.message).toHaveBeenCalledTimes(1);
    testModule.__RewireAPI__.__ResetDependency__('message');
  });
});

Lihat dokumen untuk contoh.

Catatan: Memerlukan transpilasi babel

Opsi 3 - Pisahkan semua fungsi menjadi modul/file terpisah

Opsi ini adalah yang paling tidak disukai tetapi jelas akan berfungsi dengan baik dengan fungsionalitas mock khas.


PS: Meskipun tread ini sangat mencerahkan dan sering menghibur, saya harap sinopsis ini mengurangi kebutuhan orang lain untuk membaca seluruh utas. ️

Terima kasih @nickofthyme , Anda baru saja mengakhiri beberapa hari membenturkan kepala saya terhadap ini.

@nickofthyme option 1 gagal di kedua aplikasi saya, dan create react app :

 FAIL  src/hello.test.js
  test spyon with function expressions
    ✓ should NOT mock message in foo (3ms)
    ✕ should mock message in foo (6ms)

  ● test spyon with function expressions › should mock message in foo

    expect(received).toBe(expected) // Object.is equality

    Expected: "my message"
    Received: "Hello world"

      17 |     const actual = testModule.foo();
      18 |
    > 19 |     expect(actual).toBe("my message");
         |                    ^
      20 |     expect(testModule.message).toHaveBeenCalledTimes(1);
      21 |   });
      22 | });

      at Object.toBe (src/hello.test.js:19:20)

Test Suites: 1 failed, 1 total
Tests:       1 failed, 1 passed, 2 total
Snapshots:   0 total
Time:        1.848s
Ran all test suites related to changed files.

@danielhusar Anda tampaknya benar. Maaf, saya seharusnya menguji ini dengan CRA.

Saya mendapat ejekan yang dijelaskan dalam opsi 1 untuk bekerja di sini . Uji menggunakan skrip yarn test:hello .

Hasil

> yarn test:hello
yarn run v1.16.0
$ jest --config=jest.config.js -t=hello --verbose
 PASS  src/hello/hello.test.ts
  test hello
    ✓ should NOT mock message in foo (3ms)
    ✓ should mock message in foo (1ms)

Test Suites: 1 skipped, 1 passed, 1 of 2 total
Tests:       1 skipped, 2 passed, 3 total
Snapshots:   0 total
Time:        1.392s
Ran all test suites with tests matching "hello".
✨  Done in 2.36s.

Itu membutuhkan penggunaan file jest.config.js menggunakan ts-jest dan memanggil jest --config=./jest.config.js secara langsung, bukan melalui react-scripts . Saya tidak yakin bagaimana lelucon dikonfigurasi di react-scripts tapi saya pikir mungkin ada cara untuk memperbarui konfigurasi entah bagaimana.

Perbaikan ini menghapus transformasi untuk file *.css dan *.svg , jadi abaikan kesalahan App.tsx .

Apakah ada hal khusus yang perlu dilakukan untuk membuatnya bekerja?
Saya akan mengatakan saya memiliki pengaturan yang cukup standar (tanpa ts) dan itu tidak berfungsi di luar kotak.

Saya akan melihatnya sedikit lagi malam ini dan melihat apakah itu mungkin.

@danielhusar Saya melihat ke dalam tadi malam dan tidak bisa mendapatkan solusi di luar kotak. Kuncinya adalah konfigurasi lelucon transformer yang CRA memungkinkan Anda untuk menimpa package.json#jest . File js dan ts ditranspilasikan menggunakan babel-jest tetapi react-scripts memblokir Anda dari menggunakan file konfigurasi .babelrc dan menyetel env test yang mereka atur di react-scripts test sini .

Saya berharap saya bisa menggali lebih dalam tetapi tidak punya waktu sekarang.

Hmm saya masih sedikit berjuang untuk membuatnya berfungsi (pada pengaturan khusus saya, bukan cra).
(versi terbaru dari jest dan babel-jest)

Ini adalah konfigurasi lelucon saya:

module.exports = {
  name: 'my-app',
  testURL: 'http://localhost/',
  setupFiles: ['<rootDir>/setup-jest.js'],
  setupFilesAfterEnv: ['<rootDir>/setup-test.js'],
  testMatch: ['**/__tests__/**/*.test.js?(x)', '**/?(*.)+(spec|test).js?(x)'],
  testEnvironment: 'jest-environment-jsdom-fifteen',
  snapshotSerializers: ['enzyme-to-json/serializer'],
  globals: {
    ENV: 'test',
  },
  transform: {
    '^.+\\.[t|j]sx?$': 'babel-jest',
  },
};

Dan babelrc saya:

{
  "presets": [
    "@babel/react",
    ["@babel/env", {
      "corejs": "3",
      "useBuiltIns": "entry",
      "loose": true,
      "exclude": [
        "es.string.split"
      ]
    }],
    "@babel/flow"
  ],
  "plugins": [
    "array-includes",
    "lodash",
    "@babel/plugin-syntax-dynamic-import",
    "@babel/plugin-syntax-class-properties",
    "@babel/plugin-proposal-class-properties",
    "@babel/plugin-proposal-object-rest-spread",
    "@babel/plugin-proposal-optional-chaining"
  ],
  "env": {
    "test": {
      "plugins": ["dynamic-import-node"]
    }
  }
}

Bagi siapa pun yang berjuang dengan opsi 1 , penting untuk menggunakan fungsi () => { return expression } alih-alih () => (expression) .

Saya mendapat opsi 1 untuk berfungsi dengan memodifikasinya menjadi:

import * as test from './test';

export const message = () => {
    return 'Hello world';
  }

  export const foo = () => {
    return test.message();
  }

Tidak cantik tapi harus bekerja.

@nickofthyme , Opsi 1 sudah benar seperti yang Anda miliki. Tetapi jika Anda mengubah kode menjadi:

const foo = () => {}
export { foo }

Kemudian rusak. Agaknya karena Anda membuat objek baru literal dan mengekspornya.

Pengamatan yang menarik. Terima kasih @maletor

Jest memiliki contoh yang sangat sederhana dan jelas dalam dokumentasi mereka tentang bagaimana sebagian mengejek modul. Ini berfungsi dengan pernyataan ES import dan Node require.
https://jestjs.io/docs/en/jest-object#jestrequireactualmodulename

@johncmunson Itu poin yang bagus. Namun, contoh yang Anda tunjukkan tentang mengejek modul ini hanya berfungsi jika Anda hanya perlu menjalankan jest.mock _once_ dan tidak ada metode tiruan yang menggunakan ekspor lain dari modul itu.

Ambil contoh dari atas...Saya telah menambahkan bar untuk menunjukkan bagaimana saya ingin mengejek modul secara berbeda antara foo dan bar .

export const message = (): string => {
  return 'Hello world';
}

export const foo = (): string => {
  return message();
}

export const bar = (): (() => string) => {
  return foo;
}

Menggunakan jest.mock dengan jest.requireActual Saya pikir akan seperti ini.

import * as mockTestModule from './hello';

jest.mock('./hello');
const actualTestModule = jest.requireActual('./hello');

describe('test hello', function () {
  afterAll(() => {
    jest.restoreAllMocks();
  });

  // first test doesn't depend on any other method of the module so no mocks
  it('should NOT mock message in foo', function () {
    const actual = actualTestModule.foo();

    expect(actual).toBe('Hello world');
  });

  // the second I want to mock message in foo
  it('should mock message in foo', function () {
    jest.spyOn(mockTestModule, 'message').mockReturnValue('my message');
    const actual = actualTestModule.foo();

    expect(actual).toBe('my message'); // fails
    expect(mockTestModule.message).toHaveBeenCalledTimes(1); // never called
  });

  it('should mock foo in bar', function () {
    jest.spyOn(mockTestModule, 'foo').mockReturnValue('my message');
    const actual = actualTestModule.bar();

    expect(actual()).toBe('my message'); // fails
    expect(mockTestModule.message).toHaveBeenCalledTimes(1); // never called
  });
});

Saya bahkan mencoba mengejek mereka secara terpisah dengan jest.doMock dan masih mendapatkan hasil yang sama.


Klik untuk melihat kode

```ts
import * as testModule from './hello';

deskripsikan('tes halo', fungsi() {
setelah Semua(() => {
jest.restoreAllMocks();
});

it('should NOT mock message in foo', function () {
  const actual = testModule.foo();

  expect(actual).toBe('Hello world');
});

it('should mock message in foo', function () {
  jest.doMock('./hello', () => {
    // Require the original module to not be mocked...
    const originalModule = jest.requireActual('./hello');

    return {
      ...originalModule,
      message: jest.fn().mockReturnValue('my message'),
    };
  });
  const actual = testModule.foo();

  expect(actual).toBe('my message'); // fails
  expect(testModule.message).toHaveBeenCalledTimes(1); // never called
});

it('should mock foo in bar', function () {
  jest.doMock('./hello', () => {
    // Require the original module to not be mocked...
    const originalModule = jest.requireActual('./hello');

    return {
      ...originalModule,
      foo: jest.fn().mockReturnValue('my message'),
    };
  });
  const actual = testModule.bar()();

  expect(actual).toBe('my message'); // fails
  expect(testModule.foo).toHaveBeenCalledTimes(1); // never called
});

});
```

Masalah dengan pendekatan ini adalah membutuhkan modul aktual, lalu katakan memanggil foo , masih memanggil fungsi message aktual dan bukan tiruan.

Saya berharap sesederhana ini tetapi dari apa yang saya lihat ini tidak membantu untuk contoh dari utas ini. Jika saya kehilangan sesuatu di sini, beri tahu saya. Saya akan dengan senang hati mengakui kesalahan.

Bagi siapa saja yang menemukan ini mencari solusi, berikut ini tampaknya berfungsi untuk saya ketika mengekspor banyak const/fungsi dalam satu file, dan mengimpornya dalam file yang saya uji

function mockFunctions() {
  const original = require.requireActual('../myModule');
  return {
    ...original, //Pass down all the exported objects
    test: jest.fn(() => {console.log('I didnt call the original')}),
    someFnIWantToCurry: {console.log('I will curry the original') return jest.fn((...args) => original.someFnIWantToCurry(...args)}),
  }
jest.mock('../myModule', () => mockFunctions());
const storage = require.requireMock('../myModule');
`

Berikut ini berfungsi dan sedikit lebih pendek:

const module = require('./module');
jest.spyOn(module, 'myFn').mockImplementation(() => 'val');

Di TypeScript, cukup import alih-alih require :

import * as module from './module';

Ini memiliki keuntungan membuat hidup mudah untuk mengembalikan fungsi asli dan tiruan yang jelas.

Bagi siapa saja yang menemukan ini mencari solusi, berikut ini tampaknya berfungsi untuk saya ketika mengekspor banyak const/fungsi dalam satu file, dan mengimpornya dalam file yang saya uji

function mockFunctions() {
  const original = require.requireActual('../myModule');
  return {
    ...original, //Pass down all the exported objects
    test: jest.fn(() => {console.log('I didnt call the original')}),
    someFnIWantToCurry: {console.log('I will curry the original') return jest.fn((...args) => original.someFnIWantToCurry(...args)}),
  }
jest.mock('../myModule', () => mockFunctions());
const storage = require.requireMock('../myModule');
`

Berikut ini berfungsi dan sedikit lebih pendek:

const module = require('./module');
jest.spyOn(module, 'myFn').mockImplementation(() => 'val');

Di TypeScript, cukup import alih-alih require :

import * as module from './module';

Ini memiliki keuntungan membuat hidup mudah untuk mengembalikan fungsi asli dan tiruan yang jelas.

Oh, ya juga metode ini tidak berfungsi jika objek Anda hanya memiliki getter ditentukan. Pesan kesalahannya bisa seperti di bawah ini:

Test suite failed to run

    TypeError: Cannot set property useContent of #<Object> which has only a getter

Mungkin perlu menggunakan jest.mock(..) untuk kasus ini. :bowing_man:

Mengolok-olok saya bekerja dengan menggunakan yang berikut ini:

import { unsubscribe } from "../lib/my-lib"
import { MyComponent } from "./index"

test("unsubscribe gets called", () => {
    const module = require("../lib/my-lib")
    jest.spyOn(
        module,
        "unsubscribe"
    ).mockImplementation(() => jest.fn())

    const { getByTestId } = render(() => <MyComponent  />)

    let button = getByTestId("trigger.some.action.button")

    fireEvent.press(button)

    expect(unsubscribe).toHaveBeenCalled()
})

Tampaknya tidak begitu elegan dan tidak mudah diskalakan, ini bekerja dengan sangat baik dan sesuai dengan kasus saya untuk saat ini .. tetapi jika ada yang bisa menyarankan sintaks lain yang akan bagus! Ini adalah satu-satunya sintaks yang tampaknya berfungsi.

kode modul es6:

export const funcA = () => {};
export const funcB = () => {
  funcA();
};

Setelah ditranspilasikan ke CommonJS:

"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.funcB = exports.funcA = void 0;

var funcA = function funcA() {};

exports.funcA = funcA; // You can mock or add a spy  on this `funcA`

var funcB = function funcB() {
  funcA();  // This is still original `funcA`
};

exports.funcB = funcB;

Ada banyak cara untuk mengatasi situasi ini.

  1. Anda perlu mengubah kode seperti ini, sehingga Anda dapat menggunakan mocked/spied funcA
function funcA() {}
exports.funcA = funcA;

function funcB() {
  exports.funcA(); // Now, this `exports.funcA` is added a spy or mocked. Keep the same reference to `funcA`
}
exports.funcB = funcB;

Atau,

export let funcA = () => {};
export const funcB = () => {
  exports.funcA();
};

hasil tes satuan:

 PASS  myModule.test.ts (9.225s)
  funcB
    ✓ should call funcA (3ms)

-------------|---------|----------|---------|---------|-------------------
File         | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
-------------|---------|----------|---------|---------|-------------------
All files    |     100 |      100 |     100 |     100 |                   
 myModule.ts |     100 |      100 |     100 |     100 |                   
-------------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        10.967s
  1. gunakan paket rewire untuk mengejek funcA
    ...

Selain itu, Anda perlu melihat dokumen ini: https://nodejs.org/api/modules.html#modules_exports_shortcut , untuk melihat apa sebenarnya yang dilakukan require

Solusi dalam posting stackoverflow ini berfungsi untuk saya
https://stackoverflow.com/a/53402206/1217998

Pada dasarnya pertama-tama Anda mengonversi semua fungsi yang ingin Anda konversi ke jest.fn

jest.mock('../../utils', () => {
  const actualUtils = jest.requireActual('../../utils');
  const originalImplementation = actualUtils.someFun;

  return {
    ...actualUtils,
    someFun: jest.fn()
      .mockImplementation(originalImplementation),
  };
});
const utils = require('../../utils');

Kemudian Anda dapat menggunakannya seperti biasa jika Anda mau atau mengejeknya seperti ini

jest.spyOn(utils, 'someFun').mockReturnValueOnce(true);

Anda dapat menggunakan ini untuk mendapatkan implementasi asli

beforeEach(() => {
    jest.clearAllMocks();
  });

Solusi dalam posting stackoverflow ini berfungsi untuk saya
https://stackoverflow.com/a/53402206/1217998

Pada dasarnya pertama-tama Anda mengonversi semua fungsi yang ingin Anda konversi ke jest.fn

jest.mock('../../utils', () => {
  const actualUtils = jest.requireActual('../../utils');
  const originalImplementation = actualUtils.someFun;

  return {
    ...actualUtils,
    someFun: jest.fn()
      .mockImplementation(originalImplementation),
  };
});
const utils = require('../../utils');

Kemudian Anda dapat menggunakannya seperti biasa jika Anda mau atau mengejeknya seperti ini

jest.spyOn(utils, 'someFun').mockReturnValueOnce(true);

Anda dapat menggunakan ini untuk mendapatkan implementasi asli

beforeEach(() => {
    jest.clearAllMocks();
  });

Terima kasih!

Jest memiliki contoh yang sangat sederhana dan jelas dalam dokumentasi mereka tentang bagaimana sebagian mengejek modul. Ini berfungsi dengan pernyataan ES import dan Node require.
https://jestjs.io/docs/en/jest-object#jestrequireactualmodulename

Tidak berfungsi saat fungsi yang diejek dipanggil dari dalam modul.

Juga, saya menemukan bahwa kadang-kadang berguna untuk mengejek fungsi dengan cara Anda tidak mengubah fungsi asli tetapi memanggil fungsi dengan beberapa variabel khusus (tambahan):

jest.mock('./someModule', () => {
  const moduleMock = require.requireActual('./someModule');
  return {
    ...moduleMock,
    // will mock this function 
    someFunction: (args) =>
      moduleMock.someFunction({
        ...args,
        customArgument,
      }),
  };
});

Dalam kasus saya, saya harus melewati konfigurasi come agar berfungsi tanpanya akan menggunakan konfigurasi default.

Ini adalah satu-satunya cara yang saya temukan untuk melakukan ini, jadi jika Anda menemukan beberapa ide yang lebih baik atau lebih mudah, akan senang mendengarnya :)

FWIW Saya telah mengumpulkan berbagai pendekatan dengan contoh yang dapat dijalankan di https://github.com/magicmark/jest-how-do-i-mock-x/blob/master/src/function-in-same-module/README. md

Ini tidak menjawab pertanyaan/masalah OP, tetapi merupakan solusi dengan beberapa refactoring yang terlibat. Saya telah menemukan memisahkan fungsi saya ke dalam file yang berbeda, dan kemudian mengejek impor tersebut, adalah hal termudah untuk dilakukan.

// package.json
...
"scripts": {
    "test": "jest",

...
"devDependencies": {
    "@babel/preset-env": "^7.11.5",
    "jest": "^24.9.0",
...

```js
// babel.config.js

modul.ekspor = {
preset: [
[
'@babel/preset-env',
{
target: {
simpul: 'saat ini',
},
},
],
],
};

```js
// module-utils.js

export const isBabaYaga = () => {
  return false
}

// module.js

import { isBabaYaga } from './module-utils'

export const isJohnWickBabaYaga = () => {
  return isBabaYaga()
}
// modules.test.js

import { isJohnWickBabaYaga } from './module';

jest.mock('./module-utils', () => ({
    isBabaYaga: jest.fn(() => true)
}))

test('John Wick is the Baba Yaga', () => {

    // when
    const isBabaYaga = isJohnWickBabaYaga()

    // then
    expect(isBabaYaga).toBeTruthy()
});
PASS  src/module.test.js
✓ John Wick is the Baba Yaga (4ms)

Saya baru-baru ini mengalami masalah ini. Tidak ada solusi yang diusulkan yang berfungsi untuk saya karena saya tidak dapat mengubah kode. Babel-plugin-rewire juga tidak berfungsi untuk saya. Apakah ada solusi lain di luar sana untuk menguji bahwa suatu fungsi dipanggil oleh fungsi lain dalam modul yang sama? Sejujurnya ini sepertinya harus berfungsi atau harus ada plugin babel di luar sana yang melakukan ini. Bantuan apa pun akan sangat dihargai!

Saya baru-baru ini mengalami masalah ini. Tidak ada solusi yang diusulkan yang berfungsi untuk saya karena saya tidak dapat mengubah kode. Babel-plugin-rewire juga tidak berfungsi untuk saya. Apakah ada solusi lain di luar sana untuk menguji bahwa suatu fungsi dipanggil oleh fungsi lain dalam modul yang sama? Sejujurnya ini sepertinya harus berfungsi atau harus ada plugin babel di luar sana yang melakukan ini. Bantuan apa pun akan sangat dihargai!

Sudahkah Anda memeriksa https://github.com/facebook/jest/issues/936#issuecomment -659597840? Ada repro minimal di sana yang mengolok-olok panggilan fungsi dalam file yang sama.

Apakah halaman ini membantu?
0 / 5 - 0 peringkat