Fakultas Ilmu Komputer UI

README.md 15.1 KB
Newer Older
Muhammad Ilham Peruzzi's avatar
Muhammad Ilham Peruzzi committed
1
# 1606823475-practice
2
Muhammad Ilham Peruzzi - 1606823475 - Computer Science 2016 - PMPL A - Practice Repo
Muhammad Ilham Peruzzi's avatar
Muhammad Ilham Peruzzi committed
3

4
Link Heroku : https://pmpl-peruzzi.herokuapp.com/
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68

# Cerita Exercise 3

## Isolating testing dalam Exercise 3

Dalam Lab 3 ini, test isolation dilakukan dengan memisahkan unittest dan functional test. Pada Lab sebelumnya, functional test merupakan test yang bersifat
independen dan kita harus menjalankan test tersebut secara manual. Agar unittest dan functional test dapat diautomisasi, kita harus merubah penamaan
functional_test.py menjadi test.py saja, dikarenakan test runner hanya membaca file dengan awalan test.

Untuk membuat struktur project lebih rapi, functional test yang sudah diubah namanya menjadi test.py disimpan pada direktori terpisah, bernama functional_test.
Jadi, saat kita menjalankan perintah:
```bash
python manage.py test
```
maka unittest dan functional_test akan dijalankan secara otomatis. Untuk memisahkan menjalankan test tersebut, kita bisa menggunakan perintah terpisah.

Untuk menjalankan unittest saja, bisa menggunakan perintah:
```bash
python manage.py test <nama-app>
```

Sedangkan untuk menjalankan functional test saja, bisa menggunakan perintah:
```bash
python manage.py test <folder-functional-test>
```

Selain itu, isolation test juga dilakukan dengan memisahkan server untuk melakukan functional test dengan menggunakan LiveServerTestCase.
Class tersebut akan membuat sebuah database test otomatis yang terpisah dengan database yang kita gunakan, lalu membuat development server sendiri untuk
melakukan functional testing, sehingga hasil dari functional test yang dijalankan tidak memperngaruhi aplikasi yang sedang berjalan.
Dengan demikian kita telah berhasil melakukan test isolation.

## Perbedaan Antara Design Baru dan Design Sebelumnya
- Pada desain yang baru menggunakan class LiveServerTestCase, sehingga saat menjalankan functional test tidak perlu menggunakan env heroku (baik server maupun database)
- Pada desain yang baru functional test dapat dijalankan secara otomatis dengan menggunakan perintah yang telah disebutkan diatas

## Catatan

Pada Lab 3 ini, karena saya menggunakan textbook yang berbeda dengan yang ada pada ebook di website, maka secara tidak sadar mungkin saya sudah mengerjakan
tugas untuk Lab selanjutnya, sehingga pada Lab ini saya sudah mencapai chapter 7 (https://www.obeythetestinggoat.com/book/chapter_working_incrementally.html)

# Cerita Exercise 4

Pada buku chapter 7, dijelaskan bahwa kita dapat bekerja secara incrementally, yaitu menambahkan fungsionalitas secara sedikit demi sedikit.
Maksudnya adalah untuk membuat sebuah fitur baru, kita perlu membuat testnya terlebih dahulu, membuat fungsi minimal yang membuat test passed, dan membuat implementasi lengkap dari fitur tersebut.
Pada chapter 7 juga dibahas mengenai regression test, dimana kita membuat test tanpa merusak test yang sudah ada sebelumnya.
Selain itu, pada chapter 7 juga mulai diubah struktur projectnya menjadi lebih rapi, seperti penggunaan file url terpisah, penambahan database, penggunaan template, dan lain-lain.
Pada chapter 8, dibahas bagaimana menambahkan styling pada project yang sudah ada sebelumnya. Penambahan test untuk melakukan pengecekan styling dapat merusak functional test jika project belum disesuaikan stylenya.
Sebagai contoh saat membuat test yang melakukan pengecekan posisi element berada ditengah (pada commit : https://gitlab.cs.ui.ac.id/pmpl/practice-collection/2019/1606823475-practice/commit/56b0e478068d6aafbe1be28619c6c6bde7226405), jika stylenya belum disesuaikan akan membuat functional test gagal.
Namun, test-test yang sebelumnya dibuat masih tetap passed.
Setelag disesuaikan, maka functional test akan passed.

# Cerita Exercise 5

## Keterkaitan Antara Penerapan Refactoring (Red, Green, Refactor) Dengan Konsep Clean Code

Penerapan refactoring pada konsep TDD sejalan dengan konsep clean code. Hal ini dikarenakan dengan melakukan refactoring, kita dapat mengurangi beberapa issue terkait dengan permasalahan yang ada pada clean code, contohnya issue code smells. Dengan begitu, selain code yang dibuat telah pass pada tahap testing, desain code yang dibuat dengan refactoring juga 'bersih' berdasarkan konsep clean code.

## Keuntungan yang Didapatkan Dengan Menerapkan Test Organization

Dengan menerapkan test organization, selain struktur project yang kita buat menjadi lebih rapi dan terbaca, juga membuat kita dapat menjalankan single test file sesuai dengan yang ingin kita jalankan saja. Contonya pada Latihan kali ini saya dapat menjalankan bagian tertentu saja dengan menjalankan perintah 
```bash
python manage.py test functional_tests.test_list_item_validation
```
Dengan begitu, kita tidak perlu melakukan test terhadap keseluruhan test jika hanya ingin menjalankan bagian tertentu dari test itu saja.
Muhammad Ilham Peruzzi's avatar
Muhammad Ilham Peruzzi committed
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136

# Cerita Exercise 6

## Membuat 2 Mutant dan Test Case Untuk Membunuh Mutant

Pada Lab ini saya membuat dua buah mutant dengan menggunakan metode Relational Operator Replacement (ROR) pada fungsi untuk menampilkan komentar otomatis pada list.html. Saya tidak membuat mutant pada fungsi views.py dikarenakan logic untuk menampilkan komentar terdapat pada list.html dengan menggunakan templating pada html. Mutant yang saya buat ada dua, yaitu mengubah operator '<' menjadi '>' dan mengubah operator '==' menjadi '!='. Fungsi awalnya adalah sebagai berikut.
```bash
{% if list.item_set.all|length == 0 %}   
<p>yey, waktunya berlibur</p>
{% elif list.item_set.all|length < 5 %}
<p>sibuk tapi santai</p>
{% else %}
<p>oh tidak</p>
{% endif %}
```
Mutant yang saya buat adalah sebagai berikut:
```bash
{% if list.item_set.all|length != 0 %}   
...
```

Dan

```bash
...
{% elif list.item_set.all|length > 5 %}
...
```

Setelah membuat mutant, saya membuat sebuah test case untuk membuat mutant tersebut. Test case yang saya gunakan adalah mengecek apakah komentar yang ditampilkan sesuai dengan yang seharusnya ditampilkan berdasarkan jumlah to-do yang ada. Test tersebut bisa dilihat pada file functional_test/test_automatic_comment.py. Saat saya menjalankan test tersebut (sebelum mutant dibuat), test passed. Kemudian, setelah mutant dibuat, test menjadi failed karena output yang dihasilkan berbeda dari fungsi asli, menandakan mutant telah berhasil dibunuh secara strongly killed.

## Penggunaan Mutation Testing Tools (Django-mutpy)

Untuk melakukan mutation testing dengan tools django-mutpy, pertama saya melakukan instalasi dengan perintah
```bash
pip install django-mutpy
```
Lalu memasukkan modul djago-mutpy kedalam list app pada settings.py.
Setelah itu, saya menjalankan tools django-mutpy. Hasil yang diberikan adalah sebagai berikut.
```bash
[*] Mutation score [683.53529 s]: 58.7%
   - all: 63
   - killed: 37 (58.7%)
   - survived: 26 (41.3%)
   - incompetent: 0 (0.0%)
   - timeout: 0 (0.0%)
```
Dari hasil tersebut, terlihat bahwa ada 37 mutant yang berhasil dibunuh dan 26 mutant yang masih hidup pada aplikasi yang saya buat dengan score total 58.7%. Untuk itu, saya membuat sebuah test baru untuk membunuh mutant yang masih hidup. Salah satu mutant yang masih hidup berkaitan dengan konfigurasi apliasi, sehingga saya membuat test sebagai berikut.
```bash
from django.apps import apps
from django.test import TestCase
from lists.apps import ListsConfig


class ListsConfigTest(TestCase):
    def test_apps(self):
        self.assertEqual(ListsConfig.name, 'lists')
        self.assertEqual(apps.get_app_config('lists').name, 'lists')
```
Setelah dilakukan mutation testing ulang dengan django-mutpy, hasilnya adalah sebagai berikut
```bash
[*] Mutation score [647.52812 s]: 61.9%
   - all: 63
   - killed: 39 (61.9%)
   - survived: 24 (38.1%)
   - incompetent: 0 (0.0%)
   - timeout: 0 (0.0%)
```
137
138
139
140
Terlihat score yang didapatkan meningkat menjadi 61.9% dan mutant yang survived berkurang, sehingga dapat disimpulkan ada mutant yang berhasil dibunuh setelah ditambahkan test baru tersebut.

# Cerita Exercise 7

141
142
143
144
145
146
147
148
149
150
Pada Exercise 7 ini diajarkan sebuah konsep yang dinamakan spiking dan de-spiking. Konsep spiking dan de-spiking ini adalah metode yang digunakan saat kita ingin mencoba mengimplememtasikan sebuah fitur tanpa membuat testnya terlebih dahulu. Spiking adalah kegiatan untuk melakukan implementasi tanpa membuat test terlebih dahulu untuk melihat bagaimana program bekerja dan merasakan 'feel' mengimplementasikan fitur tersebut. Pada exercise 7 ini, spiking dilakukan saat kita mencoba membuat fitur autentikasi, dikarenakan fitur autentikasi sedikit kompleks sehingga kita harus tau bagaimana mengimplementasikannya sebelum membuat test. De-spiking adalah melakukan ulang implementasi yang dilakukan saat spiking dengan menggunakan konsep TTD dan kaidah pemprogramman yang benar. Saat Exercise 7 konsep de-spiking diterapkan saat kita merevert kode dan menghapus folder acccounts untuk dibuat ulang dengan mengikuti kaidah TTD.

# Cerita Exercise 8

## Perbedaan Antara Manual Mocking dan Mocking Menggunakan Library

Pada saat membuat suatu test case, terkadang kita ingin melakukan test terhadap fungsi yang memiliki beberapa dampak eksternal, seperti penggunaan API, mengirim email, dan lain-lain. Tentu kita tidak ingin saat test dijalankan, test tersebut mengirimkan data asli ke luar program melalui internet publik. Untuk itu, diperlukan suatu teknik agar kita dapat melakukan test terhadap fungsi tersebut namun tidak memiliki eksternal efek. Teknik tersebut disebut mocking. Terdapat dua jenis cara melakukan mocking, yaitu secara manual atau menggunakan mocking library.

Saat melakukan mocking secara manual, kita menggantikan fungsi yang akan ditest dengan mock yang kita buat sendiri. Teknik ini dinamakan monkeypatching, dimana kita memodifikasi fungsi agar sesuai dengan alur program dengan data-data yang bersifat local (hanya berjalan saat test). Melakukan mocking manual harus dilakukan sebelum fungsi yang ingin di test dipanggil, agar fungsi original dapat diganti terlebih dahulu dengan mock yang dibuat.

151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
Berbeda dengan mocking manual, saat melakukan mocking dengan library kita tidak perlu melakukan setup mock sendiri, dikarenakan sudah ada library yang menyediakan dan mempersiapkan mock tersebut, sehingga kita hanya mengatur return value apa dan bagaimana mock tersebut berjalan. Salah satu library mock adalah unittest.mock dan unittest.patch yang terdapat pada Python 3.3.

## Alasan Mocking Menyebabkan Tightly Coupled

Penggunaan mocking memang sangat membantu dalam melakukan testing fungsi yang menggunakan eksternal framework. Namun, penggunaan mock bisa menyebabkan Tightly Coupled to Implementation. Hal ini disebabkan karena saat membuat mock up kita harus mengatur dan menyesuaikan mock yang dibuat dengan implementasi yang ada pada business logic (dalam hal ini views.py atau file lain yang berisi logic program). Sementara itu, mungkin saja framework yang digunakan memiliki lebih dari satu cara implementasi dengan output yang sama, sehingga mungkin program memberikan output yang sama dengan implementasi berbeda namun mocking test nya menjadi rusak. Contohnya pada implementasi mocking yang dibuat adalah sebagai berikut:

(accounts/tests/test_view.py)
```bash
@patch('accounts.views.messages')
    def test_adds_success_message_with_mocks(self, mock_messages, mock_auth):
        response = self.client.post('/accounts/send_login_email', data={
            'email': 'edith@example.com'
        })

        expected = "Check your email, we've sent you a link you can use to log in."
        self.assertEqual(
            mock_messages.success.call_args,
            call(response.wsgi_request, expected),
        )
```
Pada mock tersebut, kita melakukan mocking pada implementasi messages.success. Namun, framework Django mengizinkan implementasi penggunaan message yang lain untuk message.success dengan menggunakan message.add_message(...). Contohnya adalah sebagai berikut:
```bash
messages.add_message(
        request,
        messages.SUCCESS,
        "Check your email, we've sent you a link you can use to log in."
    )
```
179
180
181
182
183
184
Kedua implementasi tersebut, yaitu penggunaan message_success atau message.add_message akan memberikan output yang sama. Namun, dikarenakan mock yang dibuat terikat dengan message.success, maka mock yang dibuat akan rusak jika kita menggunakan implementasi message.add_message, sehingga mocking tersebut sangat bergantung pada bagaimana kita melakukan implementasi program tersebut (tightly coupled with the implementation).

# Cerita Exercise 9

## Mengapa Functional Test Subbab 20 lebih baik dari sebelumnya?

185
186
187
188
Hal tersebut dikarenakan pada subbab 20, kita menggunakan session untuk mengatur user sudah ter-autentikasi, sehingga kita tidak perlu melakukan login berulang-ulang saat melakukan test. Hal tersebut termasuk kedalam Text Fixture dimana kita melakukan 'seed' ke dalam database agar status user sudah ter-autentikasi. Selain itu, penerapan functional test pada subbab 20 juga menggunakan decorator buatan sendiri, yaitu decorator @wait yang dapat menngurangi duplikasi code pada pembuatan functional test sehingga test yang dibuat terlihat lebih rapi dan readable.

## Server-Side Debugging

189
190
191
192
193
194
195
196
197
198
199
Setelah melakukan instruksi pada subbab 21.1 dan 21.2, saya tidak melihat adanya error yang disebutkan pada textbook tersebut pada Log Heroku saya. Saya berkesimpulan mungkin saja environment yang saya gunakan berbeda dengan environment yang ada pada buku, sehingga error tersebut tidak muncul.

# Cerita Exercise 10

## Mengapa Migrasi Data Penting?

Seperti yang kita tahu, saat terjadi perubahan pada model yang kita buat, biasanya kita lebih memilih untuk menghapus semua data lama. Namun, cara ini tidak selalu baik untuk dilakukan, terlebih jika data yang sudah tersimpan sangat banyak dan merupakan data yang penting, sehingga saat terjadi perubahan pada model kita tidak bisa langsung menghapus semua data lama untuk menyesuaikan dengan perubahan model yang baru. Untuk itu, diperlukan sebuah cara agar data lama tidak perlu dihapus walaupun model berubah, salah satunya adalah dengan melakukan migrasi data. Dalam melakukan migrasi data, dibandingkan menghapus data lama, kita mengubah data lama tersebut untuk menyesuaikan dengan model yang baru.

## Simulasi Migrasi Data

Pada exercise 10 ini, text book / online book rujukan menunjukkan bagaimana jika ada data yang duplikat maka akan terjadi Integrity error. Untuk menangani hal tersebut, dibuat sebuah script dimana jika terjadi data duplikat maka salah satu dari data duplikat tersebut akan dihapus. Script ini dapat ditemukan pada file 0002_remove_duplicates.py. Setelah itu, untuk mensimulasikan migrasi data yang disebabkan oleh perubahan model, saya menambahkan field 'Slug' pada model. Data lama tentu tidak memiliki field ini, sehingga harus dilakukan migrasi data. Untuk itu, dibuat script yang akan melakukan pengisian slug secara otomatis berdasarkan isi text yang ada pada data. Script tersebut terdapat pada file 0005_add_data.py, sehingga ketika dilakukan perintah migrate maka data lama yang tidak memliki field Slug tersebut akan otomatis terisi dan kita telah selesai melakukan migrasi data. Untuk simulasi migrasi data besar, kita dapat membuat sebuah data seeding berjumlah 100 hingga 150 data tanpa field Slug (Slug=Null) kemudian di load ke dalam database. Maka ketika melakukan perintah makemigrations dan migrate data hasil seeding tersebut akan menambahkan field Slug secara otomatis berkat script yang ada pada file 0005_add_data.py