Menulis Baris Kode dengan Pendekatan TDD
Dalam mengembangkan suatu perangkat lunak, kita pasti menginginkan hasil produk yang sesuai dengan requirement dari stakeholder dan disertai kualitas yang memuaskan. Untuk melakukan hal tersebut, dalam pengembangan perangkat lunak, dikenal suatu istilah yang disebut Test Driven Development (TDD).
Apa Itu TDD?
Test Driven Development (TDD) adalah suatu pendekatan pengembangan perangkat lunak di mana developer membuat test terlebih dahulu. Test tersebut kemudian digunakan untuk mengembangkan aplikasi perangkat lunak. TDD memastikan bahwa setiap komponen dalam sistem berjalan dengan seharusnya (sesuai ekspektasi developer). Dengan menggunakan TDD, developer diarahkan untuk berpikir tentang bentuk spesifikasi input-output. Hal ini dilakukan dengan menyusun unit test sebelum menuliskan kode.
Menerapkan TDD dalam pengembangan perangkat lunak akan membuat aplikasi yang dikembangkan minim bug. Hal itu dikarenakan error dapat diketahui dan diidentifikasi dengan cepat. Selain itu, sebagai developer kita juga dimudahkan dalam proses pengembangan aplikasi. Dengan membuat test terlebih dahulu, kita akan lebih memahami perilaku program yang akan kita buat nantinya. Selain itu, pekerjaan juga menjadi lebih sederhana karena kode dibuat secara modular dan tersusun rapi.
Menguji Sesuatu yang Tidak Ada?
Dalam pengalaman saya, testing (atau automated testing) adalah sebuah proses black box. Hal tersebut berarti bahwa kita tidak perlu tahu implementasi dari apa yang sedang diuji. Sebagai contoh, unit testing adalah salah satu automated testing.
Unit testing digunakan untuk menguji fungsi tunggal dalam sebuah implementasi. Kita dapat menentukan input tertentu untuk menguji dan menentukan output yang diharapkan dari suatu fungsi. Kita tidak perlu tahu bagaimana fungsi itu berjalan, selama output yang dihasilkan sesuai dengan harapan. Dengan itu, kita dapat mengasumsikan bahwa fungsi tersebut berjalan seperti yang diharapkan atau diminta.
Setelah mengetahui konsep TDD ini, kita dapat membuat unit test terlebih dahulu sebelum membuat fungsi implementasi nya. Kita hanya perlu menentukan input dan output test yang diharapkan, bukan bagaimana cara mengimplementasikan fungsi tersebut.
Kunci dalam pengimplementasian konsep TDD ini adalah untuk bersikap percaya diri. Percaya bahwa di masa depan, kita dapat mengimplementasikan fungsi yang menghasilkan output yang diharapkan.
Implementasi TDD pada Proyek
Mata kuliah Proyek Perangkat Lunak menerapkan software methodology yang berupa TDD tersebut. TDD yang dikenal dengan red, green, refactor adalah kerangka kerja yang digunakan untuk membuat test, membuat kode implementasi, dan mengoptimasi penulisan kode dalam suatu siklus pengembangan.
Red — tahapan pembuatan test
Pada tahap ini, seorang developer menuliskan testcase yang merupakan pencerminan dari program yang ingin dikembangkan. Selain itu, developer juga membuat stubs awal berupa fungsi-fungsi yang masih belum diimplementasikan. Pesan commit yang ditulis yaitu “[RED] <masukan pesan commit>”.
Green — tahapan pembuatan kode implementasi
Pada tahap ini, developer melakukan implementasi terhadap fungsi yang ingin dibuat agar test yang telah dibuat pada tahap sebelumnya dapat berjalan dengan sukses. Pesan commit yang ditulis yaitu “[GREEN] <masukan pesan commit>”.
Refactor — tahapan peningkatan kualitas kode
Pada tahap ini, test telah berjalan dengan sukses tetapi developer masih bisa meningkatkan kualitas kode. Terkadang kodingan test yang sudah kita buat itu redundant atau tidak rapi sehingga tidak bisa mencapai code coverage 100%. Pesan commit yang ditulis yaitu “[REFACTOR] <masukan pesan commit>”.
Dalam pengembangan awal aplikasi Document Builder, saya membuat unit test yang menguji salah satu component yaitu Header. Untuk membuat test terhadap component React, saya menggunakan ‘react-testing-library’.
import React from 'react';
import { Provider } from 'react-redux';
import store from 'client/store';
import { render } from '@testing-library/react';
import Enzyme, { mount } from 'enzyme';
import Header from 'client/components/Header';
import Adapter from 'enzyme-adapter-react-16';
import ButtonHeader from 'client/components/BasicComponents/ButtonHeader';
import IconButton from '@material-ui/core/IconButton';
import MenuItem from '@material-ui/core/MenuItem';
Enzyme.configure({ adapter: new Adapter() });
describe('header component', () => {
it('renders', () => {
const { queryByTitle } = render(
<Provider store={store}>
<Header />
</Provider>
);
const navbarContainer = queryByTitle('navbar-container');
const headerContainer = queryByTitle('header-container');
const menuContainer = queryByTitle('menu-container');
const sidebarContainer = queryByTitle('sidebar-container');
expect(navbarContainer).toBeInTheDocument();
expect(navbarContainer).toContainElement(headerContainer);
expect(navbarContainer).toContainElement(sidebarContainer);
expect(headerContainer).toContainElement(menuContainer);
});
it('when the sidebar is opened', () => {
const wrapper = mount(
<Provider store={store}>
<Header />
</Provider>
);
wrapper
.find(IconButton)
.at(0)
.simulate('click');
});
it('when the sidebar is closed', () => {
const wrapper = mount(
<Provider store={store}>
<Header />
</Provider>
);
wrapper
.find(IconButton)
.at(2)
.simulate('click');
});
it('when the sidebar is closed', () => {
const wrapper = mount(
<Provider store={store}>
<Header />
</Provider>
);
wrapper
.find(IconButton)
.at(2)
.simulate('click');
});
it('when the profile button is clicked', () => {
const wrapper = mount(
<Provider store={store}>
<Header />
</Provider>
);
wrapper
.find(ButtonHeader)
.at(0)
.simulate('click');
});
it('when the profile option is selected', () => {
const wrapper = mount(
<Provider store={store}>
<Header />
</Provider>
);
wrapper
.find(MenuItem)
.at(0)
.simulate('click');
});
});
Karena saya mengharapkan test untuk berjalan gagal, maka saya membuat fungsi yang belum mengimplementasikan Header component. Berikut adalah implementasi fungsi kosong Header yang saya lakukan dengan commit [RED] create test for header component.
import React from 'react';const Header = () => {
return <div />;
};export default Header;
Setelah test dibuat dan di-push, pipeline akan menunjukkan bahwa test tersebut failed. Selanjutnya, waktunya saya mengimplementasikan fungsi Header component yang ingin dibuat agar test yang telah dibuat sebelumnya dapat berjalan dengan sukses.
Dalam proses pembuatan test serta implementasi nya, saya beberapa kali mengalami kesulitan. Walaupun saya sudah pernah menggunakan bahasa Javascript selama setahun lebih, saya belum pernah mengimplementasikan konsep TDD ini dalam pembuatan suatu program. Konsep ini merupakan hal yang baru bagi saya. Mungkin bisa dikatakan bahwa pembuatan test memakan waktu yang lebih lama dibanding mengimplementasikan fungsi dari test itu sendiri.
Dibanding dengan kelebihan yang sudah dijabarkan di atas, konsep TDD juga memiliki kekurangan yang saya rasakan dalam pengembangan proyek ini selama seminggu kemarin. Kekurangan tersebut, yaitu banyak waktu yang terbuang saat adanya perubahan kebutuhan dari klien. Selain implementasi yang diubah, kita tidak boleh melupakan test yang terintegrasi dengan implementasi tersebut.
Menurut saya, TDD adalah hal yang baik diterapkan dalam proses pengembangan perangkat lunak karena, pada akhirnya, 100% code coverage tidak menjamin baris kode kita terlepas dari bug. Namun, jika kita membuat test, setidaknya kita meminimalisir kemungkinan dari adanya bug tersebut. Apabila ada bagian kode yang berubah, test membuat kita yakin bahwa kita tidak merusak sesuatu di baris kode lainnya.