diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 0b3dd89d379f851e8898ee637a0cc7f031ad7dce..8b7bf7b6601eca993fbbb3674412b79d89ea4b12 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -13,6 +13,7 @@ Test: script: - python3 manage.py test lists + - python3 manage.py muttest lists --modules lists.views Deployment: @@ -34,12 +35,16 @@ FunctionalTest: image: python:3.7 stage: functional_test before_script: + - wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add - + - echo "deb http://dl.google.com/linux/chrome/deb/ stable main" > /etc/apt/sources.list.d/google.list + - pip3 install -r requirements.txt - apt-get update -qq && apt-get install -y -qq unzip - - apt-get install -qy --fix-missing --no-install-recommends xvfb firefox-esr + - apt-get install -y google-chrome-stable - apt-get install -y xvfb - - wget https://github.com/mozilla/geckodriver/releases/download/v0.19.0/geckodriver-v0.19.0-linux64.tar.gz; mkdir geckodriver; tar -xzf geckodriver-v0.19.0-linux64.tar.gz -C geckodriver; export PATH=$PATH:$PWD/geckodriver; - - pip3 install -r requirements.txt - - python3 manage.py migrate --noinput + - wget https://chromedriver.storage.googleapis.com/2.41/chromedriver_linux64.zip + - unzip chromedriver_linux64.zip + - python3 manage.py collectstatic --noinput + when: on_success script: diff --git a/README.md b/README.md index 9ef3c2ad0e7502898f6f92ff20c037901f1fa524..dc14db26673ab1d4e1c513e038e60274b2473d7a 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,10 @@ Herokuapp : https://pmpl-farah.herokuapp.com/ - Keterhubungan Chapter 8 dan 7 - Exercise 5 : - Keterkaitan refactoring dan clean code - - Keuntungan test organization. + - Keuntungan test organization +- Exercise 6 : + - Pembuatan Mutant + - Mutation Testing Tool : Django-mutpy ## Exercise 3 **Proses test isolation** @@ -76,3 +79,40 @@ Dengan menerapkan Red Green Refactor, berarti kita juga menerapkan konsep clean Memudahkan maintenance. Kita dapat dengan mudah menemukan test code yang kita butuhkan karena test sudah teratur dalam kategori-kategori. Selain itu kita dapat memilih test yang ingin dijalankan. Bisa menjalankan semua test, bisa juga menjalankan test spesifik saja. +## Exercise 6 +**Pembuatan Mutant** + +Pada fungsi views.py berikut adalah code implementasi fitur komentar : +``` +def view_list(request, list_id): + list_ = List.objects.get(id=list_id) + items = Item.objects.filter(list=list_) + + if len(items) == 0: + comment = 'Yey, waktunya berlibur' + elif len(items) < 5: + comment = 'Sibuk tapi santai' + elif len(items) >= 5: + comment = 'Oh tidak' + + return render(request, 'list.html', {'list': list_, 'comment':comment}) +``` +Setelah mengubah code tersebut beberapa kali, dapat dibuat satu mutant pada line 9 dengan mengubah menjadi : +`elif len(items) > 5:` + +Dengan mengubah decision pada line tersebut, test tidak error karena pada test sebelumnya tidak menghandle kondisi dimana `len(items) = 5`. Maka dari itu butuh dibuat test baru yang dapat menghandle kondisi tersebut. Jadi pada test_views.py ditambahkan function test untuk `len(items) = 5`, setelah ditambahkan test tersebut, function tersebut menjadi error, maka mutant sudah berhasil di kill. + +**Mutation Testing Tool : Django-mutpy** + +Berikut adalah hasil dari mutation testing menggunakan django-mutpy. Tool ini dijalankan pada test_models dan test_views. +``` +[0.13787 s] killed by test_passes_correct_list_to_template (lists.tests.test_views.ListViewTest) +[*] Mutation score [9.18592 s]: 100.0% + - all: 38 + - killed: 38 (100.0%) + - survived: 0 (0.0%) + - incompetent: 0 (0.0%) + - timeout: 0 (0.0%) +Destroying test database for alias 'default'... +``` +Dari hasil tersebut dapat kita lihat bahwa semua mutant sudah berhasil di kill oleh test yang ada. \ No newline at end of file diff --git a/functional_tests/base.py b/functional_tests/base.py index 2550a0dfb2a9ee18de60424756255d1dfa452c91..98de5cc48f62f5cba01f5492eb69b99288f3a28c 100644 --- a/functional_tests/base.py +++ b/functional_tests/base.py @@ -1,7 +1,7 @@ from selenium.common.exceptions import WebDriverException from django.contrib.staticfiles.testing import StaticLiveServerTestCase from selenium import webdriver -from selenium.webdriver.firefox.options import Options +from selenium.webdriver.chrome.options import Options import time import environ @@ -10,10 +10,14 @@ MAX_WAIT = 10 class FunctionalTest(StaticLiveServerTestCase): def setUp(self): - options = Options() - options.headless = True - self.browser = webdriver.Firefox(options=options) - self.browser.implicitly_wait(5) + chrome_options = Options() + chrome_options.add_argument('--dns-prefetch-disable') + chrome_options.add_argument('--no-sandbox') + chrome_options.add_argument('--headless') + chrome_options.add_argument('disable-gpu') + self.browser = webdriver.Chrome('./chromedriver', chrome_options=chrome_options) + self.browser.implicitly_wait(10) + def tearDown(self): self.browser.quit() diff --git a/functional_tests/test_simple_list_creation.py b/functional_tests/test_simple_list_creation.py index dfe370653572e2ddef4ea2809f864ebc3a80e855..3b01a3212cd5e810742040db8cd4cc09460fade0 100644 --- a/functional_tests/test_simple_list_creation.py +++ b/functional_tests/test_simple_list_creation.py @@ -1,7 +1,7 @@ from .base import FunctionalTest from selenium import webdriver from selenium.webdriver.common.keys import Keys -from selenium.webdriver.firefox.options import Options +from selenium.webdriver.chrome.options import Options @@ -70,11 +70,14 @@ class NewVisitorTest(FunctionalTest): ## We use a new browser session to make sure that no information ## of Edith's is coming through from cookies etc - self.browser.quit() - options = Options() - options.headless = True - self.browser = webdriver.Firefox(options=options) - self.browser.implicitly_wait(5) + chrome_options = Options() + chrome_options.add_argument('--dns-prefetch-disable') + chrome_options.add_argument('--no-sandbox') + chrome_options.add_argument('--headless') + chrome_options.add_argument('disable-gpu') + self.browser = webdriver.Chrome('./chromedriver', chrome_options=chrome_options) + self.browser.implicitly_wait(10) + # Francis visits the home page. There is no sign of Edith's # list diff --git a/lists/tests/test_views.py b/lists/tests/test_views.py index 0225175d0eda82f1e72008d52428133b593e2cf1..0189e90726cd934e3a857978267b95cb6a3b0b5a 100644 --- a/lists/tests/test_views.py +++ b/lists/tests/test_views.py @@ -119,7 +119,15 @@ class HomePageTest(TestCase): response = self.client.get(f'/lists/{list_.id}/') self.assertIn('Sibuk tapi santai', response.content.decode()) - def test_comment_todolist_more_than_equal_5(self): + def test_comment_todolist_equal_5(self): + list_ = List.objects.create() + for i in range(5): + Item.objects.create(text='itemey ' + str(i), list=list_) + + response = self.client.get(f'/lists/{list_.id}/') + self.assertIn('Oh tidak', response.content.decode()) + + def test_comment_todolist_more_than_5(self): list_ = List.objects.create() for i in range(8): Item.objects.create(text='itemey ' + str(i), list=list_) diff --git a/requirements.txt b/requirements.txt index f845286023c0c8311fb6fcd6b46f18b62acafdf1..a225fe3b76be81f785faa0452fe4ba4060699236 100644 --- a/requirements.txt +++ b/requirements.txt @@ -7,4 +7,5 @@ psycopg2==2.7.3.1 selenium==3.141.0 sqlparse==0.3.0 urllib3==1.25.3 -whitenoise==4.1.3 \ No newline at end of file +whitenoise==4.1.3 +django-mutpy==0.1.2 \ No newline at end of file diff --git a/superlists/settings.py b/superlists/settings.py index 8d22e1587402844d2f0eef93681bd76945a3ec03..99a6dcdb586c41ae978dd86a2b5a7a70e1494cb6 100644 --- a/superlists/settings.py +++ b/superlists/settings.py @@ -39,7 +39,8 @@ INSTALLED_APPS = [ 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', - 'lists' + 'lists', + 'django_mutpy', ] MIDDLEWARE = [