Fakultas Ilmu Komputer UI

Commit c6a75c6a authored by Rani Lasma Uli's avatar Rani Lasma Uli
Browse files

Merge branch 'master' of gitlab.cs.ui.ac.id:pmpl/practice-collection/2019/1606885025-practice

parents 5f1f3bf9 a1266cfe
......@@ -7,6 +7,7 @@
- [Cerita Exercise 4](#cerita-exercise-4)
- [Refactoring and Clean Code](#refactoring-and-clean-code)
- [Why Need Test Organization](#why-need-test-organization)
- [Mutation Testing](#mutation-testing)
- [URL](#url)
- [Install Modules](#install-modules)
......@@ -136,4 +137,83 @@ dan memindahkan semua styling ke dalam file tersebut.
### Why Need Test Organization
Test Organization menjadi penting saat test yang dibuat akan banyak. Jika semua test ditaruh dalam 1 file, debugging akan sulit. File test menjadi sangat panjang dan readability-nya berkurang. Untuk menghindari kondisi semacam ini, kita perlu mengorganisir sejumlah test ke dalam file (class) terpisah untuk memastikan helper method pada test menjadi reusable, menghindari redundant code, dan meningkatkan readability code. Readability code sangatlah penting untuk perkembangan aplikasi yang lebih besar, seperti penambahan fitur atau perubahan requirement yang biasa terjadi pada penerapan Agile Methodology. Jadi, kesadaran untuk menjaga readability code harus disadari sedari awal pengembangan.
\ No newline at end of file
Test Organization menjadi penting saat test yang dibuat akan banyak. Jika semua test ditaruh dalam 1 file, debugging akan sulit. File test menjadi sangat panjang dan readability-nya berkurang. Untuk menghindari kondisi semacam ini, kita perlu mengorganisir sejumlah test ke dalam file (class) terpisah untuk memastikan helper method pada test menjadi reusable, menghindari redundant code, dan meningkatkan readability code. Readability code sangatlah penting untuk perkembangan aplikasi yang lebih besar, seperti penambahan fitur atau perubahan requirement yang biasa terjadi pada penerapan Agile Methodology. Jadi, kesadaran untuk menjaga readability code harus disadari sedari awal pengembangan.
### Mutation Testing
Berikut adalah kode yang akan di-test berikut dengan 2 mutant yang valid.
```python
def index(request):
if request.method == 'POST' and request.POST['item_text'] != '':
Item.objects.create(text=request.POST['item_text'])
return redirect('/')
items = Item.objects.all()
message = "oh tidak"
# Mutant 1: items.count() != 0
if items.count() == 0:
message = "yey, waktunya berlibur"
# Mutant 2: items.count() <= 5
elif items.count() < 5:
message = "sibuk tapi santai"
return render(request, 'to-do-list.html', {
'items': items,
'comment':message,
})
```
Untuk membunuh Mutant 1 dengan kategori `Strongly Kill` jumlah item harus memenuhi lebih dari 5. Mutant 1 akan mengeluarkan output `yey, waktunya libur`, sedangkan method index() yang sesungguhnya akan mengeluarkan output `oh tidak`. Berikut implementasinya.
```python
def test_to_kill_Index_mutant1(self):
Item.objects.all().delete()
# 6 items
for x in range(0,6):
Item.objects.create(text='itemey '+str(x))
response = self.client.get(TO_DO_LIST_URL)
html = response.content.decode('utf8')
self.assertTrue("oh tidak" in html)
```
Lalu untuk membunuh Mutant 2, dibutuhkan jumlah item tepat 5, sehingga Mutant 2 akan mengeluarkan output `sibuk tapi santai`, sedangkan method index() yang sebenarnya akan mengeluarkan output `oh tidak`. Berikut adalah implemetasinya.
```python
def test_to_kill_Index_mutant2(self):
Item.objects.all().delete()
# 5 items
for x in range(0,5):
Item.objects.create(text='itemey '+str(x))
response = self.client.get(TO_DO_LIST_URL)
html = response.content.decode('utf8')
self.assertTrue("oh tidak" in html)
```
#### Django-mutpy
Automasi dari mutation testing dapat dilakukan dengan bantuan library Django-mutpy. Library ini berguna untuk memproduksi mutant. Setiap mutant yang dapat dibunuh dengan unittest yang telah dibuat akan meningkatkan mutant score. Dalam hal ini mutant score yang saya miliki rendah oleh karena mutasi yang dilakukan mayoritas untuk perihal data migration dan appconfig. Akan tetapi, salah satu dari mutant tersebut ada yang masih `survive` dengan unittest saya. Berikut adalah mutant yang masih hidup.
```python
from lists.models import Item
5: response = {}
6: def index(request):
- 7: if request.method == 'POST':
+ 7: if not (request.method == 'POST'):
8: Item.objects.create(text=request.POST['item_text'])
9: return redirect('/')
10:
11: items = Item.objects.all()
```
Untuk membunuh mutant ini, saya menambah test seperti berikut ini.
```python
def test_GET_will_return_ok_response(self):
response = self.client.get(TO_DO_LIST_URL)
self.assertEqual(response.status_code, 200)
self.assertFalse('location' in response)
```
Hal ini akan membuat mutant menghasilkan kode response 302, sedangkan kode yang sebenarnya akan mengembalikan kode response 200.
\ No newline at end of file
......@@ -25,7 +25,6 @@ class ListViewTest(TestCase):
def test_can_save_a_POST_request(self):
response = self.client.post(TO_DO_LIST_URL, data={'item_text': 'A new list item'})
self.assertEqual(Item.objects.count(), 1)
new_item = Item.objects.first()
self.assertEqual(new_item.text, 'A new list item')
......@@ -37,6 +36,11 @@ class ListViewTest(TestCase):
self.assertEqual(response.status_code, 302)
self.assertEqual(response['location'], TO_DO_LIST_URL)
def test_GET_will_return_ok_response(self):
response = self.client.get(TO_DO_LIST_URL)
self.assertEqual(response.status_code, 200)
self.assertFalse('location' in response)
def test_only_saves_items_when_necessary(self):
self.client.get(TO_DO_LIST_URL)
self.assertEqual(Item.objects.count(), 0)
......@@ -69,6 +73,33 @@ class ListViewTest(TestCase):
html = response.content.decode('utf8')
self.assertIn("oh tidak", html)
# Mutant 1 output: yey, waktunya libur --> isinya 6, harusnya oh tidak
def test_to_kill_Index_mutant1(self):
Item.objects.all().delete()
# 6 items
for x in range(0,6):
Item.objects.create(text='itemey '+str(x))
response = self.client.get(TO_DO_LIST_URL)
html = response.content.decode('utf8')
self.assertTrue("oh tidak" in html)
# Mutant 2 output: sibuk tapi santai --> isinya 5, harusnya oh tidak
def test_to_kill_Index_mutant2(self):
Item.objects.all().delete()
# 5 items
for x in range(0,5):
Item.objects.create(text='itemey '+str(x))
response = self.client.get(TO_DO_LIST_URL)
html = response.content.decode('utf8')
self.assertTrue("oh tidak" in html)
......
......@@ -11,8 +11,11 @@ def index(request):
items = Item.objects.all()
message = "oh tidak"
# Mutant 1: items.count() != 0
if items.count() == 0:
message = "yey, waktunya berlibur"
# Mutant 2: items.count() <= 5
elif items.count() < 5:
message = "sibuk tapi santai"
......@@ -21,3 +24,44 @@ def index(request):
'comment':message,
})
# MUTANT IMPLEMENTATION
# def index_mutant1(request):
# if request.method == 'POST' and request.POST['item_text'] != '':
# Item.objects.create(text=request.POST['item_text'])
# return redirect('/')
# items = Item.objects.all()
# message = "oh tidak"
# # !!! mutant of items.count() == 0
# if items.count() != 0:
# message = "yey, waktunya berlibur"
# elif items.count() < 5:
# message = "sibuk tapi santai"
# return render(request, 'to-do-list.html', {
# 'items': items,
# 'comment':message,
# })
# def index_mutant2(request):
# if request.method == 'POST' and request.POST['item_text'] != '':
# Item.objects.create(text=request.POST['item_text'])
# return redirect('/')
# items = Item.objects.all()
# message = "oh tidak"
# if items.count() == 0:
# message = "yey, waktunya berlibur"
# # !!! mutant of items.count() < 5
# elif items.count() <= 5:
# message = "sibuk tapi santai"
# return render(request, 'to-do-list.html', {
# 'items': items,
# 'comment':message,
# })
astmonkey==0.3.6
chromedriver==2.24.1
coverage==4.5.4
dj-database-url==0.5.0
Django==1.11.24
django-heroku==0.3.1
django-mutpy==0.1.2
gunicorn==19.9.0
Jinja2==2.10.3
MarkupSafe==1.1.1
MutPy==0.6.0
psycopg2==2.8.3
psycopg2-binary==2.8.3
pydot==1.4.1
pyparsing==2.4.4
pytz==2019.2
PyYAML==5.1.2
selenium==3.141.0
termcolor==1.1.0
urllib3==1.25.3
whitenoise==4.1.3
......@@ -50,6 +50,7 @@ INSTALLED_APPS = [
'django.contrib.staticfiles',
'lists',
'homepage',
'django_mutpy',
]
MIDDLEWARE = [
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment