diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 914b5e3a93defe154eb01ad61d8748cf0f2aa735..622386ec716f1b43da784fa5508e3a125edc02a9 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,6 +1,7 @@ stages: - test - staging + - functionalTest UnitTest: image: python:3.6 @@ -31,11 +32,7 @@ Deployment: FunctionalTest: image: python:3.6 -<<<<<<< HEAD - stage: staging -======= stage: functionalTest ->>>>>>> d07aa98ddd4bc378e56653f8fd225d1c2cc723f1 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 @@ -48,6 +45,6 @@ FunctionalTest: - wget -O /tmp/chromedriver.zip http://chromedriver.storage.googleapis.com/2.11/chromedriver_linux64.zip && unzip /tmp/chromedriver.zip chromedriver -d /usr/local/bin/; when: on_success script: - - python functional_tests.py + - python manage.py test functional_tests except: - master diff --git a/README.md b/README.md index 449c0c8fdd7237d174a149acd075f2b61deacbb9..df0fcddbc5b5f95c932f25b2a26e05bc405babe1 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,16 @@ # 1606885025-practice -Rani Lasma Uli - 1606885025 - Computer Science 2016 - PMPL A - Practice Repo +*Rani Lasma Uli - 1606885025 - Computer Science 2016 - PMPL A - Practice Repo* -## URL: +## Outline + - [Cerita Exercise 3](#cerita-exercise-3) + - [URL](#url) + - [Install Modules](#install-modules) + +### URL http://pmpl-ranisianipar.herokuapp.com -## Install Modules +### Install Modules - activate the virtual environment Windows: \env\Scripts\activate.bat @@ -15,3 +20,74 @@ pip install -r requirements.txt - update module to requirements.txt pip freeze > requirements.txt + +### Cerita Exercise 3 +This section contains exercise 3 details which refer to the obeythetestinggoat chapter 6. + +#### Steps I've been through +1. Refactoring <br> +sebelum mengimplementasikan `test isolation` dilakukan pemindahan file functional_tests.py ke dalam folder functional_tests dan mengubah nama file menjadi tests.py (functional_tests/tests.py). Django membutuhkan __init__.py untuk menjadi Python package yang valid, sehingga perlu ditambahkan file functional_tests/__init__.py. Hal ini berguna untuk pemanggilan functional_tests yang lebih mudah menjadi --> `python manage.py test functional_tests` + +2. Using Python Test runner <br> +Untuk menggunakan Python Test runner class yang digunakan pada functional_tests/tests.py diubah dari UnitTest menjadi LiveServerTestCase. Untuk kode tes yang lebih baik, URL tidak perlu lagi menggunakan *hard code* untuk `localhost:8000`. LiveServerTestCase menyediakan atribut bawaan untuk URL app yang akan diuji, yaitu method *live_server_url*. Atribut tersebut akan dipanggil menjadi `self.live_server_url`. + +3. Explicit to Implicit waiting <br> +Pada chapter 5, functional_tests.py menunggu selama 1 detik untuk output data. Namun pada chapter 6 ini, data akan dipanggil sampai hasilnya diperoleh. Waktu tunggu maksimal adalah 10 detik. Kode yang kali ini lebih efektif karena waktu tunggu untuk hasil lebih dinamis (menyesuaikan lama waktu elemen diperoleh). + +*Where's the Test Isolation implemented?* <br> +Test Isolation sendiri adalah mengisolasi setiap tes untuk menghilangkan ketergantungannya satu sama lain antar tes. Pada awalnya kita menjalankan functional_tests.py memengaruhi database `item` yang ada. Hal ini tidak baik karena akan mengganggu nilai yang akan diuji oleh tes yang lain. Untuk mempersiapkan database setiap test method dapat menggunakan `setUp` dan `tearDown` method. Selain dari cara itu, untuk Django 1.4++ disediakan class LiveServerTestCase yang secara otomatis menyediakan test database. + +#### Before 'n After + +1. `localhost:8000` or any hard code URL (staging URL) --> self.live_server_url <br> +- Before +```python +self.browser.get('localhost:8000' + TO_DO_LIST_PATH) +``` + +- After +```python +self.browser.get(self.live_server_url + TO_DO_LIST_PATH) +``` +2. Add a helper method which will be responsible for the `implicit waiting`, also define maximum time as the waiting time limit <br> +solution: add wait_for_row_in_list_table method +- After +```python +# GLOBAL VARIABLE IN THIS CLASS +MAX_WAIT = 10 + def wait_for_row_in_list_table(self, row_text): + start_time = time.time() + while True: + try: + table = self.browser.find_element_by_id('id_list_table') + rows = table.find_elements_by_tag_name('tr') + self.assertIn(row_text, [row.text for row in rows]) + return + except (AssertionError, WebDriverException) as e: + if time.time() - start_time > MAX_WAIT: + raise e + time.sleep(0.5) +``` +3. Call the helper method <br> +- Before +```python +def test_can_start_a_list_and_retrieve_it_later(self): + ... + time.sleep(1) + ... + ... + self.check_for_row_in_list_table('1: Buy peacock feathers') + self.check_for_row_in_list_table('2: Use peacock feathers to make a fly') + ... +``` +- After +```python +def test_can_start_a_list_and_retrieve_it_later(self): + ... + self.wait_for_row_in_list_table('1: Buy peacock feathers') + ... + ... + # The page updates again, and now shows both items on her list + self.wait_for_row_in_list_table('2: Use peacock feathers to make a fly') + self.wait_for_row_in_list_table('1: Buy peacock feathers') +``` \ No newline at end of file diff --git a/functional_tests/__init__.py b/functional_tests/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/functional_tests.py b/functional_tests/tests.py similarity index 56% rename from functional_tests.py rename to functional_tests/tests.py index 83a06c9055452280f9c5387771e025d864884ccf..6f1a6c2e42ae5dd64e5809f0421adeb5ad91e9c8 100644 --- a/functional_tests.py +++ b/functional_tests/tests.py @@ -1,11 +1,13 @@ +from selenium.common.exceptions import WebDriverException +from django.test import LiveServerTestCase from selenium import webdriver from selenium.webdriver.common.keys import Keys import time import unittest -URL = "http://pmpl-ranisianipar.herokuapp.com/" -TO_DO_LIST_PATH = "to-do-list/" -class NewVisitorTest(unittest.TestCase): +TO_DO_LIST_PATH = "/to-do-list" +MAX_WAIT = 10 +class NewVisitorTest(LiveServerTestCase): def setUp(self): self.browser = webdriver.Chrome() @@ -18,11 +20,11 @@ class NewVisitorTest(unittest.TestCase): self.assertIn(row_text, [row.text for row in rows]) def test_can_access_homepage_by_url(self): - self.browser.get(URL) + self.browser.get(self.live_server_url) self.assertIn('Homepage', self.browser.title) def test_can_start_a_list_and_retrieve_it_later(self): - self.browser.get(URL + TO_DO_LIST_PATH) + self.browser.get(self.live_server_url + TO_DO_LIST_PATH) # Edith has heard about a cool new online to-do app. She goes # to check out its homepage @@ -40,30 +42,23 @@ class NewVisitorTest(unittest.TestCase): # She types "Buy peacock feathers" into a text box (Edith's hobby # is tying fly-fishing lures) - inputbox.send_keys('Buy peacock feathers') - - # When she hits enter, the page updates, and now the page lists - # "1: Buy peacock feathers" as an item in a to-do list table - inputbox.send_keys(Keys.ENTER) - time.sleep(1) + inputbox.send_keys('Buy peacock feathers') - ''' - # There is still a text box inviting her to add another item. She - # enters "Use peacock feathers to make a fly" (Edith is very - # methodical) - ''' + # When she hits enter, the page updates, and now the page lists + # "1: Buy peacock feathers" as an item in a to-do list table + inputbox.send_keys(Keys.ENTER) + self.wait_for_row_in_list_table('1: Buy peacock feathers') + + # There is still a text box inviting her to add another item. She + # enters "Use peacock feathers to make a fly" (Edith is very + # methodical) inputbox = self.browser.find_element_by_id('id_new_item') inputbox.send_keys('Use peacock feathers to make a fly') inputbox.send_keys(Keys.ENTER) - time.sleep(1) - table = self.browser.find_element_by_id('id_list_table') - rows = table.find_elements_by_tag_name('tr') - # The page updates again, and now shows both items on her list - self.check_for_row_in_list_table('1: Buy peacock feathers') - self.check_for_row_in_list_table('2: Use peacock feathers to make a fly') - + self.wait_for_row_in_list_table('2: Use peacock feathers to make a fly') + self.wait_for_row_in_list_table('1: Buy peacock feathers') # There is still a text box inviting her to add another item. She # enters "Use peacock feathers to make a fly" (Edith is very @@ -72,6 +67,15 @@ class NewVisitorTest(unittest.TestCase): # The page updates again, and now shows both items on her list - -if __name__ == '__main__': - unittest.main(warnings='ignore') + def wait_for_row_in_list_table(self, row_text): + start_time = time.time() + while True: + try: + table = self.browser.find_element_by_id('id_list_table') + rows = table.find_elements_by_tag_name('tr') + self.assertIn(row_text, [row.text for row in rows]) + return + except (AssertionError, WebDriverException) as e: + if time.time() - start_time > MAX_WAIT: + raise e + time.sleep(0.5) \ No newline at end of file