Sinon: Menghentikan konstruktor tidak berfungsi

Dibuat pada 6 Sep 2018  ·  10Komentar  ·  Sumber: sinonjs/sinon

Jelaskan bugnya
Tidak memicu konstruktor yang terhenti

Untuk Mereproduksi
Jalankan tes berikut, itu akan memberikan AssertError: expected constructor to be called once but was called 0 times :

  import sinon from 'sinon';

  class FooBar {
    constructor() {
      console.log("hello");
    }

    f() {
      console.log('f');
    }
  }

  describe('Example', () => {
    it('stubs constructor', () => {
      sinon.stub(FooBar.prototype, 'constructor').callsFake();
      new FooBar();
      sinon.assert.calledOnce(FooBar.prototype.constructor); // fails
    });

    it('stubs method', () => {
      sinon.stub(FooBar.prototype, 'f').callsFake();
      const a = new FooBar();
      a.f();
      sinon.assert.calledOnce(FooBar.prototype.f); // passes
    });
  });

Perilaku yang diharapkan
Tes pertama lulus (konstruktor yang dimatikan dipanggil)

Tangkapan layar
Jika berlaku, tambahkan tangkapan layar untuk membantu menjelaskan masalah Anda.

Konteks (harap lengkapi informasi berikut):

  • Versi perpustakaan: 5.0.7
  • Lingkungan: macOS sierra
  • Pustaka lain yang Anda gunakan: mocha

Komentar yang paling membantu

Pemikiran Anda masuk akal, tetapi implementasinya menggunakan elemen yang tidak digunakan lagi.

( __proto__ 's) keberadaan dan perilaku yang tepat hanya distandarisasi dalam spesifikasi ECMAScript 2015 sebagai fitur warisan
Referensi MDN

Pada dasarnya, Anda tidak boleh menggunakannya dalam kode Anda.

Ada alasan mengapa fungsi _inherits Anda memposting cuplikan dari mencoba menggunakan Object.setPrototypeOf jika tersedia, hanya kembali menggunakan __proto__ di browser yang sangat lama. Alasannya adalah bahwa ia bekerja.

Object.setPrototypeOf(B, sinon.stub().callsFake(function() { console.log('I am Super Stub!'); } ));
new B(); // prints am Super Stub!
Object.getPrototypeOf(B).callCount // 1

Lihat inti ini untuk lebih lanjut. Juga ini jika Anda bertanya-tanya tentang warisan ES5.

Semua 10 komentar

Anda mematikan referensi ke konstruktor ( prototype.constructor ). Tidak mungkin untuk mematikan konstruktor itu sendiri karena kendala bahasa.

Anda dapat menyuntikkan konstruktor dan kemudian menyuntikkan rintisan biasa dalam pengujian Anda untuk memverifikasi bahwa itu dipanggil dengan new .

@RenWenshan Masalah ini telah muncul beberapa kali, tetapi itu tidak ada hubungannya dengan Sinon itu sendiri, tetapi hanya karena kurangnya pemahaman tentang apa yang dirujuk oleh kata kunci constructor di ES6 (petunjuk: ia berbagi sangat sedikit dengan Function#constructor selain nama).

Apa yang dapat kami lakukan sebagai organisasi untuk memperbaiki situasi adalah dengan menulis tutorial dan dokumentasi, sesuatu yang membutuhkan sedikit usaha, itulah sebabnya kami memiliki masalah untuk mengatasi hal ini (#1121). Sampai itu terwujud dengan sendirinya, saya sarankan membaca

Pengetahuan itu akan membuatnya jauh lebih mudah untuk bekerja dengan kode ES6 Anda (dan yang lebih baru), karena Anda tahu apa yang _benar-benar_ terjadi.

@fatso83 terima kasih banyak telah menjelaskan ini.

Sekarang saya melihat bahwa constructor adalah gula sintaks dan tidak ada hubungannya dengan Function.prototype.constructor, oleh karena itu mematikannya tidak berpengaruh.

Kebutuhan awal saya adalah mematikan konstruktor kelas induk kelas sehingga saya dapat menguji apakah super dipanggil, misalnya

class Foo {
  constructor() {
    console.log("Foo");
  }
}

class FooBar extends Foo {
  constructor() {
    super();
    console.log("FooBar");
  }
}

Karena implementasi yang mendasari _inherits meliputi:

    Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;

Saya pikir saya dapat mematikan __proto__ dari Foo ? Tapi sepertinya tidak berhasil:

    const stub = sinon.stub(FooBar, '__proto__').callsFake(() => {
      console.log("stub");
    });

    const foo = new FooBar(); // still outputs "Foo\nFooBar"

Pemikiran Anda masuk akal, tetapi implementasinya menggunakan elemen yang tidak digunakan lagi.

( __proto__ 's) keberadaan dan perilaku yang tepat hanya distandarisasi dalam spesifikasi ECMAScript 2015 sebagai fitur warisan
Referensi MDN

Pada dasarnya, Anda tidak boleh menggunakannya dalam kode Anda.

Ada alasan mengapa fungsi _inherits Anda memposting cuplikan dari mencoba menggunakan Object.setPrototypeOf jika tersedia, hanya kembali menggunakan __proto__ di browser yang sangat lama. Alasannya adalah bahwa ia bekerja.

Object.setPrototypeOf(B, sinon.stub().callsFake(function() { console.log('I am Super Stub!'); } ));
new B(); // prints am Super Stub!
Object.getPrototypeOf(B).callCount // 1

Lihat inti ini untuk lebih lanjut. Juga ini jika Anda bertanya-tanya tentang warisan ES5.

@fatso83 Terima kasih untuk intinya. Itu membantu saya mematikan kelas.
Tapi bagaimana cara memata-matai kelas dengan benar? Saya mencoba berbagai cara, tetapi satu-satunya cara saya benar-benar dapat memata-matai konstruktor adalah sebagai berikut:

  it('test sinon constructor spy with container', () => {
    const testClassContainer = {};
    testClassContainer.TestClass = class TestClass {
      constructor() {
        console.log('testClass created');
      }
    }
    const a = sinon.spy(testClassContainer, 'TestClass');
    console.log('called', a.called); // false
    new testClassContainer.TestClass();
    console.log('called', a.called); // true
  });

Itu terlihat seperti pertanyaan penggunaan; silakan kirim ke StackOverflow dan beri tag dengan sinon , sehingga komunitas yang lebih besar dapat membantu menjawab pertanyaan Anda.

Lebih banyak orang mengikuti tag yang akan membantu Anda.

Inilah yang bekerja untuk saya:

test('constructor', async () => {
  const constructorStub = sinon.stub()

  function MyClass (...args) {
    return constructorStub(...args)
  }

  new MyClass({ some: 'args' })

  sinon.assert.calledWith(constructorStub, { some: 'args' })
})

@simoneb Apakah Anda yakin itu masuk akal simon? Anda baru saja mengimplementasikan seluruh objek untuk diuji sebagai sesuatu yang mengembalikan sebuah rintisan. Itu adalah sesuatu yang tidak mungkin/masuk akal dalam sistem produksi apa pun.

Ini bukan "menghentikan konstruktor", btw. Ini membuat konstruktor yang mengembalikan fungsi dan menguji jika fungsi yang dikembalikan dipanggil. Konstruktor masih MyClass - dan itu bukan rintisan (juga tidak bisa).

@simoneb Apakah Anda yakin itu masuk akal simon? Anda baru saja mengimplementasikan seluruh objek untuk diuji sebagai sesuatu yang mengembalikan sebuah rintisan. Itu adalah sesuatu yang tidak mungkin/masuk akal dalam sistem produksi apa pun.

Ini bukan "menghentikan konstruktor", btw. Ini membuat konstruktor yang mengembalikan fungsi dan menguji jika fungsi yang dikembalikan dipanggil. Konstruktor masih MyClass - dan itu bukan rintisan (juga tidak bisa).

Ya ini tidak mematikan konstruktor tetapi ketika digunakan bersama dengan sesuatu seperti proxyquire, yang dapat Anda gunakan untuk mengejek ekspor modul, Anda dapat mengganti konstruktor perpustakaan eksternal dengan MyClass dan memeriksa bagaimana konstruktor dipanggil. Aku seharusnya lebih jelas.

Bagaimana kalau mengganti seluruh kelas sebagai rintisan saja?

import sinon from 'sinon';
import * as exampleModule from 'exampleModule';

describe('example', function(){
  it(function() {
    const classStubbedInstance = Sinon.createStubInstance(exampleModule.ExampleClass);
    const constructorStub = Sinon.stub(module, 'ExampleClass').returns(classStubbedInstance);

    sinon.assert.calledWith({some: 'args'})
  })
)

Sejauh ini berhasil untuk beberapa kasus sederhana untuk saya.

Apakah halaman ini membantu?
0 / 5 - 0 peringkat