Tes unit sangat penting dalam pengembangan perangkat lunak. Terutama ketika membangun aplikasi besar, kami ingin memastikan bahwa semua modul yang berbeda berfungsi sebagaimana mestinya.

Berdasarkan definisinya, pengujian unit membantu kita memastikan satu unit kode melakukan tugasnya. Oleh karena itu, _untuk membuat pengujian unit yang baik, kita harus memastikan bahwa kita mengisolasi sebanyak mungkin bagian kode yang ingin kita uji_; kami tidak ingin modul lain (dependensi yang diperlukan) mengganggu hasil modul yang kami uji.

Namun, mungkin agak sulit untuk meniru setiap ketergantungan yang dibutuhkan sebuah modul. Di situlah __Proxyquire__ beraksi.

__Proxyquire__ adalah modul praktis yang _memungkinkan kita mem-proxy semua persyaratan modul saat mengimpornya_. Jika kita menggabungkannya dengan __Sinon__ (modul Node JS lain yang berguna untuk pengujian), kita sekarang dapat dengan mudah menghentikan ketergantungan apa pun yang diperlukan oleh unit yang kita uji, sehingga memudahkan untuk fokus pada pengujian perilakunya secara terpisah.

## Bagaimana itu bekerja

Mari kita gunakan contoh untuk mengilustrasikan bagaimana __Proxyquire__ dan __Sinon__ bekerja sama. Bayangkan kita memiliki aplikasi besar dan kita memerlukan modul untuk mengambil/menyimpan pengaturan pengguna dari/ke dalam file. Mari buat modul itu beserta pengujian unitnya yang sesuai.

Kode untuk modul manajer pengaturan ini adalah sebagai berikut (validasi dan penanganan kesalahan dihilangkan demi kesederhanaan):

```
const fs = memerlukan('fs/janji');

module.exports = () =› {
const load = async () =› {
coba {
const rawSettings = menunggu fs.readFile('settings.json');
return JSON.parse(rawSettings);
} catch (kesalahan) {
return {};
}
};

const save = async (pengaturan = {}) =› {
const settingsStr = JSON.stringify(settings);
fs.writeFile('settings.json', settingsStr);
};

kembalikan {
memuat,
menyimpan
};
};
```

Seperti yang bisa kita lihat, modul baru kita bergantung pada modul _file system_ bawaan Node JS. Oleh karena itu, perilaku modul eksternal ini dapat mempengaruhi pengujian unit untuk modul manajer pengaturan yang baru saja kita buat. Idealnya, kita tidak ingin hal itu terjadi, karena: 1) perilaku modul _fs_ (atau modul lain apa pun yang diperlukan oleh unit pengujian kita) mungkin berada di luar kendali kita (misalnya, koneksi ke API eksternal, database, file sistem, dll.) dan 2) kami ingin memfokuskan pengujian unit kami pada satu unit (bukan ketergantungannya).

Jadi, mari kita lihat bagaimana kita dapat menguji unit modul kita dengan meniru dependensinya dan menghindari perilaku yang tidak diinginkan.

(Catatan: Untuk contoh ini, kita akan menggunakan __Mocha__ + __Chai__ sebagai kerangka pengujian + pustaka. Kemudian, kita menambahkan __Sinon__ dan __Proxyquire__ untuk meniru dependensi modul manajer pengaturan dan mengujinya secara terpisah.)

Hal pertama yang pertama: untuk menginstal semua dependensi dev yang akan kita gunakan untuk pengujian, kita dapat menggunakan perintah berikut (atau menginstal setiap dependensi satu per satu):

```
npm install mocha chai sinon sinon-chai proxyquire
```

Kemudian muncul kodenya (beberapa kasus uji dihilangkan demi kesederhanaan):

```
const chai = memerlukan('chai');
chai.use(memerlukan('sinon-chai'));
const { mengharapkan } = chai;
const sinon = memerlukan('sinon');
const proxyquire = memerlukan('proxyquire').noCallThru();

deskripsikan('Pengaturan manajer', () =› {
const settingsPath = 'settings.json';
biarkan settingsFake;
biarkan fsFake;
biarkan settingsManager;

beforeEach(() =› {
settingsFake = {
bahasa: ‘Bahasa Inggris’
};

// [1] Buat modul 'fs' palsu dengan Sinon.
fsFake = {
// [2] Matikan fungsi 'readFile'.
readFile: sinon.stub(). callFake(path =› JSON.stringify(settingsFake)),
// [3] Matikan fungsi 'writeFile'.
writeFile: sinon.stub().callsFake((path, settings) =› {
settingsFake = JSON.parse(pengaturan)
})
};

// [4] Muat modul manajer pengaturan dengan Proxyquire.
settingsManager = proxyquire('../../settingsManager', {
'fs/promises': fsFake
})( );
});

it('memuat pengaturan', async () =› {
const settings = menunggu settingsManager.load();

// Periksa apakah fungsi yang di-stub telah dipanggil dengan benar.
expect(fsFake.readFile).to.have.been.callOnceWith(settingsPath);
// Periksa apakah pengaturan yang dikembalikan sudah benar.
mengharapkan(settings).to.be.an('object').that.deep.equals(settingsFake);
});

it('menyimpan pengaturan', async () =› {
const defaultSettings = {
bahasa: 'Bahasa Inggris'
};
const updateSettings = {
bahasa: 'Jepang'
};
const updateSettingsStr = JSON.stringify(updatedSettings);

// Pertama, periksa apakah pengaturan asli sama dengan pengaturan default.
let settings = menunggu settingsManager.load();
expect(settings).to.be.an('object'). that.deep.equals(defaultSettings);

// Lalu, perbarui pengaturannya…
waiting settingsManager.save(updatedSettings);
// ..dan periksa apakah fungsi yang di-stub telah dipanggil dengan benar.
expect(fsFake.writeFile).to .have.been.callOnceWith(settingsPath, updateSettingsStr);

// Terakhir, periksa apakah pengaturan telah diperbarui dengan benar.
settings = menunggu settingsManager.load();
expect(settings).to.be.an('object').that.deep.equals (Pengaturan yang diperbarui);
});
});
```

Berikut penjelasan singkat tentang apa yang terjadi:

Pertama, kita membuat modul '_fs_' palsu dengan __Sinon__ (1) yang berisi versi stub dari fungsi yang kita gunakan dalam modul kita. Kami memiliki fungsi '_readFile_' (2) yang dihentikan dengan perilaku yang disederhanakan: ia menerima jalur sebagai parameter (yang sepenuhnya diabaikan) dan hanya mengembalikan pengaturan yang dirangkai palsu; dan kami juga memiliki fungsi '_writeFile_' (3) yang dihentikan dengan perilaku yang disederhanakan: ia menerima jalur dan pengaturan untuk disimpan sebagai parameter dan hanya memperbarui pengaturan palsu dengan pengaturan yang diurai. Kami tidak menguji fungsi modul '_fs_' ini, jadi kami dapat menyederhanakan perilakunya seperti yang kami harapkan agar modul kami berfungsi dengan baik.

Terakhir, kita memuat modul manajer pengaturan dengan __Proxyquire__ (4) untuk memasukkan dependensi yang telah di-stub, sehingga mencegah dependensi asli dimuat.

Dengan cara ini kita dapat menghindari perilaku tak terduga dari modul yang diperlukan oleh unit pengujian kita dan fokus pada pengujian fungsinya sendiri. Kami sekarang dapat menjalankan pengujian unit sebanyak yang kami inginkan dan mengharapkan hasil yang sama setiap saat.

Begitulah cara kita meniru dependensi untuk membuat pengujian unit di Node JS dengan __Proxyquire__ dan __Sinon__!

Terima kasih sudah membaca!

Kata-kata oleh Ricardo Mendoza, Insinyur Utama di Altimetrik

Bahan:
- [Repositori Github Proxyquire](https://github.com/thlorenz/proxyquire)
- [Dokumentasi Sinon](https://sinonjs.org/releases/latest/)