Ini adalah contoh sepele yang menggambarkan inti permasalahan saya:
var innerLib = require('./path/to/innerLib');
function underTest() {
return innerLib.doComplexStuff();
}
module.exports = underTest;
Saya mencoba menulis unit test untuk kode ini. Bagaimana saya bisa mengejek persyaratan untuk innerLib
tanpa mengolok-olok require
fungsi sepenuhnya?
Jadi ini saya mencoba untuk mengejek global require
dan mengetahui bahwa itu tidak akan berhasil bahkan untuk melakukan itu:
var path = require('path'),
vm = require('vm'),
fs = require('fs'),
indexPath = path.join(__dirname, './underTest');
var globalRequire = require;
require = function(name) {
console.log('require: ' + name);
switch(name) {
case 'connect':
case indexPath:
return globalRequire(name);
break;
}
};
Masalahnya adalah bahwa require
fungsi di dalam underTest.js
file sebenarnya belum dipermainkan. Itu masih menunjuk ke require
fungsi global . Jadi sepertinya saya hanya bisa mengejek require
fungsi dalam file yang sama dengan yang saya lakukan mengejek. Jika saya menggunakan global require
untuk memasukkan apa pun, bahkan setelah saya mengganti salinan lokal, file yang diminta masih memiliki require
referensi global .
sumber
global.require
. Variabel menulismodule
secara default karena modul memiliki cakupan modul.Jawaban:
Sekarang kamu bisa!
Saya menerbitkan proxyquire yang akan mengatasi kebutuhan global di dalam modul Anda saat Anda mengujinya.
Ini berarti Anda tidak perlu mengubah kode Anda untuk menyuntikkan ejekan untuk modul yang diperlukan.
Proxyquire memiliki api yang sangat sederhana yang memungkinkan penyelesaian modul yang Anda coba untuk menguji dan meneruskan ejekan / bertopik untuk modul yang diperlukan dalam satu langkah sederhana.
@Raynos benar bahwa secara tradisional Anda harus menggunakan solusi yang tidak terlalu ideal untuk mencapai itu atau melakukan pengembangan bottom-up sebagai gantinya
Yang merupakan alasan utama mengapa saya membuat proxyquire - untuk memungkinkan pengembangan yang didorong oleh pengujian top-down tanpa kesulitan.
Lihat dokumentasi dan contoh untuk mengukur apakah itu sesuai dengan kebutuhan Anda.
sumber
proxyquire
!Pilihan yang lebih baik dalam hal ini adalah dengan mengejek metode modul yang akan dikembalikan.
Untuk lebih baik atau lebih buruk, sebagian besar modul node.js adalah lajang; dua potong kode yang memerlukan () modul yang sama mendapatkan referensi yang sama ke modul itu.
Anda dapat memanfaatkan ini dan menggunakan sesuatu seperti sinon untuk mengejek item yang diperlukan. Tes mocha berikut:
Sinon memiliki integrasi yang baik dengan chai untuk membuat pernyataan, dan saya menulis modul untuk mengintegrasikan sinon dengan moka untuk memungkinkan pembersihan mata-mata / rintisan yang lebih mudah (untuk menghindari uji polusi.)
Perhatikan bahwa underTest tidak dapat diejek dengan cara yang sama, karena underTest hanya mengembalikan fungsi.
Pilihan lain adalah menggunakan Jest mock. Ikuti di halaman mereka
sumber
require('some_module')
, karena semua kode Anda membagikan dir node_modules yang sama. Kedua, artikel ini menggabungkan namespace dengan lajang, yang semacam ortogonal. Ketiga, artikel itu sudah sangat tua (sejauh node.js yang bersangkutan) sehingga apa yang mungkin telah valid kembali pada hari itu mungkin tidak valid sekarang.innerLib.toCrazyCrap.restore()
dan restub, atau hubungi sinon viasinon.stub(innerLib, 'toCrazyCrap')
yang memungkinkan Anda untuk mengubah bagaimana berperilaku rintisan:innerLib.toCrazyCrap.returns(false)
. Selain itu, rewire tampaknya sangat mirip denganproxyquire
ekstensi di atas.Saya menggunakan mock-butuhkan . Pastikan Anda mendefinisikan tiruan Anda sebelum Anda
require
menguji modul.sumber
Mengejek
require
terasa seperti hack jahat bagi saya. Saya pribadi akan mencoba menghindarinya dan memperbaiki kode untuk membuatnya lebih dapat diuji. Ada berbagai pendekatan untuk menangani ketergantungan.1) lulus dependensi sebagai argumen
Ini akan membuat kode dapat diuji secara universal. Kelemahannya adalah Anda harus melewati dependensi, yang dapat membuat kode terlihat lebih rumit.
2) mengimplementasikan modul sebagai kelas, kemudian menggunakan metode / properti kelas untuk mendapatkan dependensi
(Ini adalah contoh yang dibuat-buat, di mana penggunaan kelas tidak masuk akal, tetapi menyampaikan gagasan itu) (contoh ES6)
Sekarang Anda dapat dengan mudah mematikan
getInnerLib
metode untuk menguji kode Anda. Kode menjadi lebih verbose, tetapi juga lebih mudah untuk diuji.sumber
colors
modul populer yang mengacaukanString.prototype
)Jika Anda pernah menggunakan jest, maka Anda mungkin akrab dengan fitur tiruan jest.
Dengan menggunakan "jest.mock (...)" Anda cukup menentukan string yang akan muncul dalam pernyataan-kebutuhan dalam kode Anda di suatu tempat dan setiap kali modul diperlukan menggunakan string itu, objek-mock akan dikembalikan sebagai gantinya.
Sebagai contoh
akan sepenuhnya mengganti semua impor / persyaratan "firebase-admin" dengan objek yang Anda kembalikan dari "pabrik" -fungsi.
Nah, Anda bisa melakukannya saat menggunakan jest karena jest menciptakan runtime di sekitar setiap modul yang dijalankannya dan menyuntikkan versi "doyan" dari kebutuhan ke dalam modul, tetapi Anda tidak akan bisa melakukan ini tanpa bercanda.
Saya telah mencoba untuk mencapai ini dengan mock-butuhkan tetapi bagi saya itu tidak berhasil untuk tingkat bersarang di sumber saya. Lihat masalah berikut di github: mock-need tidak selalu dipanggil dengan Mocha .
Untuk mengatasi ini, saya telah membuat dua modul npm yang dapat Anda gunakan untuk mencapai apa yang Anda inginkan.
Anda memerlukan satu babel-plugin dan pengejek modul.
Dalam .babelrc Anda gunakan plugin babel-plugin-mock-require dengan opsi berikut:
dan dalam file pengujian Anda gunakan modul jestlike-mock seperti:
The
jestlike-mock
modul ini masih sangat belum sempurna dan tidak memiliki banyak dokumentasi tapi tidak ada banyak kode baik. Saya menghargai setiap PR untuk set fitur yang lebih lengkap. Tujuannya adalah untuk membuat ulang seluruh fitur "jest.mock".Untuk melihat bagaimana jest mengimplementasikan seseorang dapat mencari kode dalam paket "jest-runtime". Lihat https://github.com/facebook/jest/blob/master/packages/jest-runtime/src/index.js#L734 misalnya, di sini mereka menghasilkan "automock" modul.
Semoga itu bisa membantu;)
sumber
Kamu tidak bisa Anda harus membangun unit test unit Anda sehingga modul terendah diuji terlebih dahulu dan modul tingkat lebih tinggi yang membutuhkan modul diuji setelahnya.
Anda juga harus berasumsi bahwa kode pihak ketiga dan node.js sendiri sudah teruji dengan baik.
Saya kira Anda akan melihat kerangka mengejek tiba dalam waktu dekat yang menimpa
global.require
Jika Anda benar-benar harus menyuntikkan mock Anda dapat mengubah kode Anda untuk mengekspos ruang lingkup modular.
Berhati-hatilah karena ini akan memaparkan
.__module
API Anda dan kode apa pun dapat mengakses lingkup modular dengan bahaya sendiri.sumber
Anda dapat menggunakan perpustakaan tiruan :
sumber
Kode sederhana untuk mengejek modul untuk yang penasaran
Perhatikan bagian di mana Anda memanipulasi
require.cache
dan perhatikanrequire.resolve
metode karena ini adalah saus rahasia.Gunakan seperti :
TAPI ... proxyquire sangat luar biasa dan Anda harus menggunakannya. Itu membuat Anda memerlukan override terlokalisasi untuk tes saja dan saya sangat merekomendasikannya.
sumber