Sinon: Rintisan sederhana gagal untuk impor ES6

Dibuat pada 27 Feb 2018  ·  18Komentar  ·  Sumber: sinonjs/sinon

  • Versi Sinon : 4.4.2
  • Lingkungan: MacOSX High Sierra 10.13.3
  • Contoh URL:
  • Pustaka lain yang Anda gunakan: Webpack, babel, mocha

Apa yang Anda harapkan terjadi?
Diberikan modul seperti ini:

// mod1.js
function test() {
  return 'fail';
}

export {
  test
};

Dan tes seperti ini:

// mod1Spec.js
import { assert } from 'chai';
import sinon from 'sinon';

import * as mod1 from 'Modules/settings/mod1';

describe('test', function () {
  it('should correctly mock module method', () => {
    sinon.stub(mod1, 'test').returns('pass');
    assert.strictEqual(mod1.test(), 'pass');
  });
});

Tes harus lulus.

Apa yang sebenarnya terjadi?
Metode ini tidak pernah diejek. Itu masih mengembalikan 'gagal' bahkan dengan rintisan langsung di atasnya. Saya telah melihat beberapa masalah seputar mengejek modul ES6, tetapi semuanya tampaknya menyiratkan bahwa menggunakan import * as blah akan memungkinkan Anda untuk mematikan dengan benar (kelas ES6 mungkin cerita yang berbeda).

Bisakah Anda mematikan modul ES6 dengan sinon? Jika demikian, apakah ada perpustakaan yang diketahui dapat mengganggu stubbing? Saya membayangkan babel atau webpack akan menjadi penyebab yang valid karena mereka dapat membundel/mengubah modul dan merusak stubbing.

Saya telah mengobrak-abrik kode saya, jadi saya ingin mengonfirmasi bahwa mungkin untuk mematikan modul ES6 tanpa menggunakan apa pun seperti babel-plugin-rewire atau jika plugin seperti itu sekarang diperlukan.

Cara memperbanyak
Lihat kode di atas.

Komentar yang paling membantu

Saya telah membuat tes yang dapat dijalankan dari contoh Anda, menggunakan @std/esm alih-alih babel .

Ini menunjukkan dengan sangat jelas bahwa (di bawah simpul), kami tidak diizinkan untuk memodifikasi modul yang diimpor menggunakan import . Sepertinya impor adalah tampilan hanya-baca pada ekspor .

Jadi, sinon.stub tidak dapat mengubah modul yang diimpor.

Saya kira Anda harus menggunakan beberapa jahitan tautan dengan pemuat modul pilihan Anda untuk mendefinisikan kembali apa arti dependensi.

Jika Anda akan menggunakan link seam , Anda mungkin tertarik dengan api sinon.fake yang baru (instal sebagai sinon@next ). Api masih dalam pengembangan, umpan balik sangat diterima.

Saya ingin tahu apakah ada cara bagi kami untuk mendeteksi bahwa kami menggunakan modul, dan dengan demikian tidak dapat mengganti properti pada impor. Jika ada, setidaknya kami dapat memberikan pesan kesalahan yang berguna kepada pengguna, sehingga mereka tidak perlu menghabiskan banyak waktu untuk mencari tahu mengapa segala sesuatunya gagal dengan cara yang tidak terduga.

Semua 18 komentar

Saya telah membuat tes yang dapat dijalankan dari contoh Anda, menggunakan @std/esm alih-alih babel .

Ini menunjukkan dengan sangat jelas bahwa (di bawah simpul), kami tidak diizinkan untuk memodifikasi modul yang diimpor menggunakan import . Sepertinya impor adalah tampilan hanya-baca pada ekspor .

Jadi, sinon.stub tidak dapat mengubah modul yang diimpor.

Saya kira Anda harus menggunakan beberapa jahitan tautan dengan pemuat modul pilihan Anda untuk mendefinisikan kembali apa arti dependensi.

Jika Anda akan menggunakan link seam , Anda mungkin tertarik dengan api sinon.fake yang baru (instal sebagai sinon@next ). Api masih dalam pengembangan, umpan balik sangat diterima.

Saya ingin tahu apakah ada cara bagi kami untuk mendeteksi bahwa kami menggunakan modul, dan dengan demikian tidak dapat mengganti properti pada impor. Jika ada, setidaknya kami dapat memberikan pesan kesalahan yang berguna kepada pengguna, sehingga mereka tidak perlu menghabiskan banyak waktu untuk mencari tahu mengapa segala sesuatunya gagal dengan cara yang tidak terduga.

Ini di luar cakupan dari apa yang seharusnya dilakukan sinon, karena berhubungan dengan seluruh rentang kompleksitas yang berada di luar kendali kita. Jika Anda membuka codepen ini di Chrome dan membuka konsol, Anda akan melihat bahwa tidak mungkin mengubah ekspor modul _dalam sistem yang sepenuhnya sesuai dengan spesifikasi_ (yang bukan Babel, tetapi Chrome), menghasilkan

Tidak dapat menetapkan untuk hanya membaca properti 'pengujian' objek '[Modul objek]'

Babel adalah masalah lain sepenuhnya, karena kode yang dihasilkan saat mentranspilasikan modul ES6 ke modul ES5 CommonJS tidak sesuai dengan spesifikasi modul ES6 (waktu parse vs ekspor waktu evaluasi dinamis, dll), dan kurang lebih merupakan detail internal Babel. Itu berarti kode di atas _should_ berfungsi, tetapi saya cukup mengacaukannya hanya dengan menyiapkan Codepen sederhana itu untuk mendemonstrasikan pemuatan ES6 sehingga saya tidak akan masuk ke lubang kelinci tambahan itu ...

Lihat tips yang disebutkan di #1623 untuk melangkah lebih jauh ( proxyquire atau rewire mungkin diperlukan). Menutup karena di luar jangkauan.

@mroderick Saya menggunakan 2 jam dari mulai menjawab ini sampai benar-benar mengirimkan balasan, jadi tidak melihat balasan Anda sampai setelah saya mengirimkannya. Jika ini benar-benar permintaan fitur untuk pesan kesalahan yang lebih baik, saya pikir kita harus membuka masalah terpisah untuk membuat pembacaan sedikit lebih jelas.

Jika ini benar-benar permintaan fitur untuk pesan kesalahan yang lebih baik, saya pikir kita harus membuka masalah terpisah untuk membuat pembacaan sedikit lebih jelas.

Saya sedang mencari tahu apakah mungkin untuk mendeteksi ketika berjalan dalam mode yang sesuai dengan ESM. AFAICT, tidak.

Saya kira kita dapat membuat peningkatan dengan membungkus penggantian properti dalam try-catch , dan kemudian memberikan pesan kesalahan yang lebih baik. Haruskah kita melakukan itu?

Untuk kasus seperti di atas, di mana kita melakukan transpile, pendeteksian mungkin tidak akan berhasil, tetapi untuk ESM bukankah seharusnya kita hanya dapat melakukan pemeriksaan tipe pada objek menggunakan toString dan === '[object Module]' ?

bukankah seharusnya kita bisa melakukan pemeriksaan tipe pada objek menggunakan toString dan === '[object Module] '?

Ide bagus

Dalam eksperimen saya (di simpul 8.9.4), impor modul tidak memiliki metode toString , dan menggunakan Object.prototype.toString mengembalikan [object Object] .

Mungkin saya melewatkan sesuatu. Apakah Anda memiliki contoh kerja ini?

Saya tidak - saya hanya berasumsi metode toString() adalah tempat kesalahan (disebutkan di atas) mendapatkan bit [object Module] . Saya dapat mencoba melihat ke dalamnya sedikit.

OK, lihat ke dalamnya, dan saya pikir Anda melewatkan sesuatu :smirk: Perilaku yang saya lihat di Chrome direproduksi di Node 6.12, 8.0, 8.7 dan 9.2. Saya menemukan info yang diperlukan dalam spesifikasi Ecma 262 untuk Modul dan cuplikan ini di MDN .

Saya pada dasarnya memotong repo Anda dan menambahkan beberapa tes untuk memverifikasinya:

  the Module object
    ✓ should have a toStringTag symbol with the expected value
    ✓ should use the toStringTag symbol 

Ini seharusnya perubahan yang sepele, jadi mungkin saya bisa memberikan PR untuk membuat kesalahan untuk itu?

@fatso83 @mroderick Terima kasih banyak atas balasannya. Saya benar-benar mengerti bahwa ini di luar lingkup sinon jadi saya menghargai bantuan dalam memahami masalah ini.

Saya sangat menyukai gagasan peringatan/kesalahan ketika pengguna mencoba menghentikan modul yang diimpor menggunakan import . Mudah-mudahan itu akan membantu siapa pun yang mencoba melakukan hal yang sama seperti yang saya lakukan.

Saya harus menjelajahi alternatif atau berpotensi tetap menggunakan RequireJS dan tidak "meningkatkan" ke impor/ekspor ES6.

Tarik permintaan dalam antrian: #1715.

Lihat perbedaannya, @ctaylo21 , untuk melihat apakah ini kira-kira yang Anda harapkan _jika berjalan di sistem pendukung Modul ES_.

Saya tidak berpikir ini akan membantu Anda dalam kasus spesifik Anda, sayangnya, karena Modul ES yang ditranskripsikan bukan lagi Modul ES yang sebenarnya ...

@ ctaylo21 Seperti yang saya sebutkan di atas, saya terkejut mendengar bahwa kode yang Anda berikan tidak berfungsi setelah transpiling. Saya baru saja mencoba membuat ulang masalah dengan proyek baru yang menggunakan Babel untuk mengubah contoh Anda dan berfungsi dengan baik, jadi saya curiga Anda tidak melakukan _ persis_ apa yang ditunjukkan oleh kode contoh itu?

Saya menduga Anda telah menjadi korban masalah yang sama seperti #1248 dan #1648 yang pada dasarnya berkaitan dengan bagaimana seseorang dalam pengujian dapat menghentikan ekspor dalam modul yang digunakan oleh modul _another_. Ini pada dasarnya adalah masalah CJS, jadi bacalah itu menggunakan tautan di bawah, karena Anda dapat mencapainya tanpa proxyquire dan "memerlukan middleware" serupa ( bekerja dengan _link seams_ ). Saya pikir Anda akan menemukan tautan ini menarik untuk mencapai apa yang Anda inginkan jika Anda ingin menghindari ketergantungan tambahan:

@fatso83 Saya dapat mengonfirmasi bahwa cabang Anda tidak memberikan pengecualian ketika saya mencoba mematikan modul yang diimpor dalam kode saya. Saya memverifikasi cek

if (
       object &&
       typeof Symbol !== "undefined" &&
       object[Symbol.toStringTag] === "Module"
   ) {
       throw new TypeError("No support for stubbing ES Modules");
   }

salah dan saya berasumsi ini mungkin karena Babel mengubah kode.

Saya tidak tahu mengapa pengaturan bersih Anda berfungsi, tetapi pengaturan saya tidak. Saya harus mendalami itu.

Masalah utama saya adalah saya mencoba memutakhirkan basis kode besar dari sistem build lawas. Sebagai bagian dari refactor itu, saya berharap untuk menggunakan impor/ekspor dan berhenti menggunakan RequireJS. Injeksi ketergantungan dan/atau middleware adalah solusi yang valid, tetapi kemungkinan akan membutuhkan banyak penulisan ulang dari ribuan tes. Sepertinya opsi kami adalah menulis ulang banyak modul kami, menggunakan sambungan tautan, atau tetap menggunakan RequireJS.

Sekali lagi terima kasih atas semua bantuan Anda, saya sangat menghargainya.

Jika Anda memutuskan untuk menggunakan jahitan tautan yang menargetkan pemuat modul, inilah favorit saya

Saya tahu bahwa ada solusi serupa ketika menggunakan Webpack, tetapi saya tidak memiliki cukup pengalaman baru-baru ini dengan Webpack untuk merekomendasikan solusi apa pun untuk itu.

Jika saya dihadapkan dengan tantangan Anda, saya akan melakukan sedikit eksperimen untuk melihat apakah saya bisa mendapatkan sesuatu dan berjalan dengan System.js, karena dapat menggunakan ketiga sistem modul. Ini akan memungkinkan Anda untuk memfaktorkan ulang bagian-bagian modul dari kode secara perlahan, alih-alih mem-porting seluruh basis kode sekaligus, dan berisiko membuat seluruh sistem tidak stabil.

Alat lain yang mungkin berguna di gudang senjata Anda: codemod dapat membantu Anda menulis ulang semua kode AMD ke CommonJS/ESM, termasuk kode untuk pengujian.

@fatso83 Tampaknya tes dasar saya gagal karena pengaturan modul di babel. Saya mengaturnya ke false yang memberi tahu Babel untuk tidak mengonversi format modul. Default mengubah modul menjadi format CommonJS, yang tampaknya menjadi alasan repo sampel Anda dengan babel berfungsi.

Karena pengocokan pohon dengan Webpack tidak berfungsi pada modul tanpa struktur statis, pengaturan yang disarankan adalah menonaktifkan transformasi modul di Babel (selama Anda menggunakan sintaks ESM). Jadi kebanyakan orang yang menggunakan webpack dan babel kemungkinan akan melihat masalah yang sama seperti saya.

@ctaylo21 Mungkin Anda menjalankan Babel 5, karena pengaturan itu tidak lagi didukung. Mengaktifkannya di repo saya memberikan ini:
ReferenceError: [BABEL] src/mod1.js: Using removed Babel 5 option: foreign.modules - Use the corresponding module transform plugin in the plugin option. Check out http://babeljs.io/docs/plugins/#modules

Hari-hari ini tampaknya seseorang harus secara eksplisit mengaktifkan transformasi tertentu, tetapi saya tidak yakin bagaimana menggali lebih jauh ...

Hm, saya pikir saya punya paket terbaru dengan Babel. Mungkin kombinasi paket atau sesuatu. Either way, tidak apa-apa untuk menjatuhkan ini. Saya mengerti mengapa masalah saya terjadi dan mengapa babel mungkin dapat "memperbaikinya" karena dapat mengubah modul ke format yang berbeda. Saya sangat menghargai semua bantuan dan saran.

Saya menghadapi masalah yang sama.
a.ts :

export function funcA(args) {
  return function c() {};
}

funB.ts :

import * as a from './a';

export const funcB = () => a.funcA({ arg: 123 });

funcB.test.ts :

import { expect } from 'chai';
import sinon from 'sinon';
import * as a from './a';
import { funcB } from './funcB';

describe('57796168', () => {
  let sandbox: sinon.SinonSandbox;
  before(() => {
    sandbox = sinon.createSandbox();
  });
  it('should call a.funcA', () => {
    const funcAStub = sandbox.stub(a, 'funcA');
    funcB();
    expect(funcAStub.calledWith({ args: 123 })).to.be.true;
  });
});

Hasil tes satuan:

  57796168
    1) should call a.funcA


  0 passing (16ms)
  1 failing

  1) 57796168
       should call a.funcA:

      AssertionError: expected false to be true
      + expected - actual

      -false
      +true

      at Context.<anonymous> (src/stackoverflow/57796168/funcB.test.ts:14:54)

@mrdulin Ini adalah masalah tertutup, artinya telah diselesaikan. Juga tidak jelas _what_ Anda juga melihat, secara eksplisit, karena tidak jelas di lingkungan mana Anda menjalankan kode (Node murni, transformasi Babel, Webpack, ... dll).

Jika Anda memerlukan bantuan untuk menyelesaikan sesuatu, harap kirimkan ke StackOverflow dan beri tag sinon , sehingga komunitas yang lebih besar dapat membantu menjawab pertanyaan Anda.

Jika Anda merasa topik Anda adalah masalah _baru_ yang sebenarnya dengan Sinon, silakan buka tiket baru dan ikuti panduan untuk melaporkan masalah .

Sebelum Anda melakukannya, harap baca utas ini dengan saksama dan cobalah untuk memahami apa yang sebenarnya dapat dilakukan Sinon, dan apa yang berada di luar cakupan Sinon (seperti kerumitan yang disebabkan oleh bundel modul Anda, seperti Webpack atau Babel).

Apakah halaman ini membantu?
0 / 5 - 0 peringkat