Apakah praktik yang buruk untuk unit test saling bergantung?

9

Katakanlah saya memiliki semacam unit test seperti ini:

let myApi = new Api();

describe('api', () => {

  describe('set()', () => {
    it('should return true when setting a value', () => {
      assert.equal(myApi.set('foo', 'bar'), true);
    });
  });

  describe('get()', () => {
    it('should return the value when getting the value', () => {
      assert.equal(myApi.get('foo'), 'bar');
    });
  });

});

Jadi sekarang saya punya 2 unit tes. Satu menetapkan nilai dalam API. Yang lain menguji untuk memastikan nilai yang tepat dikembalikan. Namun tes 2 tergantung pada yang pertama. Haruskah saya menambahkan .set()metode dalam tes ke-2 sebelum get()dengan tujuan tunggal untuk memastikan tes ke-2 tidak tergantung pada hal lain?

Juga, dalam contoh ini saya harus instantiating myApiuntuk setiap tes daripada melakukannya sekali sebelum tes?

Jake Wilson
sumber

Jawaban:

16

Ya, itu praktik buruk. Tes unit harus berjalan secara independen satu sama lain, untuk alasan yang sama bahwa Anda memerlukan fungsi lain untuk berjalan secara independen: Anda dapat memperlakukannya sebagai unit independen.

Haruskah saya menambahkan metode .set () di tes ke-2 sebelum get () dengan tujuan tunggal untuk memastikan tes ke-2 tidak tergantung pada hal lain?

Iya. Namun, Jika ini hanya metode pengambil dan penyetel yang telanjang, metode tersebut tidak mengandung perilaku apa pun, dan Anda benar-benar tidak perlu mengujinya, kecuali jika Anda memiliki reputasi untuk hal-hal yang menggerakkan lemak sedemikian rupa sehingga pengambil / penyetel mengkompilasi tetapi menetapkan atau mendapatkan bidang yang salah.

Robert Harvey
sumber
Dalam contoh saya, katakanlah myApiadalah objek instantiated. Haruskah saya mengaktifkan kembali myApidalam setiap unit test? Atau menggunakannya kembali di antara tes berpotensi menyebabkan tes untuk memberikan positif palsu, dll. Dan ya contoh saya adalah hal pengambil / penyetel yang disederhanakan tetapi pada kenyataannya itu jelas akan jauh lebih rumit.
Jake Wilson
1
@JakeWilson Ada sebuah buku berjudul Pragmatic Unit Testing yang membahas dasar-dasar pengujian unit, seperti cara menghindari tes yang saling mengganggu, dll.
rwong
Contoh lain: jika Anda menggunakan JUnit, urutan fungsi Anda tidak ditentukan oleh urutan yang Anda tulis di kelas. @JakeWilson Jika api Anda stateless, Anda dapat menggunakannya kembali, jika tidak mengembalikannya.
Walfrat
2

Coba ikuti struktur mengatur-tindakan-tegas untuk setiap tes.

  1. Atur objek Anda, dll. Dan masukkan ke dalam kondisi yang diketahui (perlengkapan uji). Kadang-kadang, fase ini termasuk menegaskan untuk menunjukkan bahwa Anda sebenarnya dalam keadaan yang Anda pikirkan.
  2. Bertindak, yaitu: melakukan perilaku yang Anda uji.
  3. Tegaskan bahwa Anda mendapatkan hasil yang diharapkan.

Tes Anda tidak perlu repot-repot membuat negara yang dikenal pertama, sehingga tidak ada artinya dalam isolasi.

Selain itu, tes unit tidak harus menguji metode tunggal saja - tes unit harus menguji unit. Biasanya, unit ini adalah kelas. Beberapa metode seperti get()hanya masuk akal dalam kombinasi dengan yang lain.

Pengujian getter dan setter masuk akal, khususnya dalam bahasa dinamis (hanya untuk memastikan mereka benar-benar ada). Untuk menguji seorang pengambil, kita perlu memberikan nilai yang diketahui terlebih dahulu. Ini mungkin terjadi melalui konstruktor, atau melalui setter. Atau dipandang berbeda: menguji pengambil secara implisit dalam tes setter dan konstruktor. Dan setter, apakah selalu kembali true, atau hanya ketika nilainya diubah? Ini mungkin mengarah pada tes berikut (pseudocode):

describe Api:

  it has a default value:
    // arrange
    api = new Api()
    // act & assert
    assert api.get() === expected default value

  it can take custom values:
    // arrange & act
    api = new Api(42)
    // act & assert
    assert api.get() === 42

  describe set:

    it can set new values:
      // arrange
      api = new Api(7)
      // act
      ok = api.set(13)
      // assert
      assert ok === true:
      assert api.get() === 13

    it returns false when value is unchanged:
      // arrange
      api = new Api(57)
      // act
      ok = api.set(57)
      // assert
      assert ok === false
      assert api.get() === 57

Menggunakan kembali keadaan dari tes sebelumnya akan membuat tes kami cukup rapuh. Menyusun ulang tes atau mengubah nilai pasti dalam suatu tes dapat menyebabkan tes yang tampaknya tidak terkait gagal. Dengan asumsi negara tertentu juga dapat menyembunyikan bug jika menyebabkan tes lulus yang seharusnya gagal. Untuk mencegah hal ini, beberapa pelari ujian memiliki opsi untuk menjalankan kasus uji secara acak.

Namun, ada beberapa kasus saat kami menggunakan kembali negara yang disediakan oleh tes sebelumnya. Khususnya saat membuat perlengkapan uji memerlukan banyak waktu, kami menggabungkan banyak kasus uji ke dalam rangkaian uji. Walaupun tes ini lebih rapuh, mereka mungkin masih lebih berharga sekarang karena mereka dapat dilakukan lebih cepat dan lebih sering. Dalam praktiknya, menggabungkan tes diinginkan setiap kali tes melibatkan komponen manual, ketika database besar diperlukan, atau ketika menguji mesin negara.

amon
sumber