From 0715b5ed528ecfcbaaf1d9a591d0f90e2e7d4905 Mon Sep 17 00:00:00 2001 From: Farah Alhaniy Date: Mon, 14 Oct 2019 13:25:46 +0700 Subject: [PATCH 1/4] Moved Fts into their own individual files --- functional_tests/base.py | 37 +++++++++++ functional_tests/test_layout_and_styling.py | 28 ++++++++ functional_tests/test_list_item_validation.py | 24 +++++++ ...{tests.py => test_simple_list_creation.py} | 65 +------------------ 4 files changed, 92 insertions(+), 62 deletions(-) create mode 100644 functional_tests/base.py create mode 100644 functional_tests/test_layout_and_styling.py create mode 100644 functional_tests/test_list_item_validation.py rename functional_tests/{tests.py => test_simple_list_creation.py} (65%) diff --git a/functional_tests/base.py b/functional_tests/base.py new file mode 100644 index 0000000..88b97f1 --- /dev/null +++ b/functional_tests/base.py @@ -0,0 +1,37 @@ +from selenium.common.exceptions import WebDriverException +from django.contrib.staticfiles.testing import StaticLiveServerTestCase +from selenium import webdriver +from selenium.webdriver.firefox.options import Options +import time +import environ + +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) + + def tearDown(self): + self.browser.quit() + + 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) + + def check_for_row_in_list_table(self, row_text): + 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]) \ No newline at end of file diff --git a/functional_tests/test_layout_and_styling.py b/functional_tests/test_layout_and_styling.py new file mode 100644 index 0000000..97c06d5 --- /dev/null +++ b/functional_tests/test_layout_and_styling.py @@ -0,0 +1,28 @@ +from .base import FunctionalTest +from selenium.webdriver.common.keys import Keys + +class LayoutAndStylingTest(FunctionalTest): + + def test_layout_and_styling(self): + # Edith goes to the home page + self.browser.get(self.live_server_url) + self.browser.set_window_size(1024, 768) + + # She notices the input box is nicely centered + inputbox = self.browser.find_element_by_id('id_new_item') + self.assertAlmostEqual( + inputbox.location['x'] + inputbox.size['width'] / 2, + 512, + delta=10 + ) + # She starts a new list and sees the input is nicely + # centered there too + inputbox.send_keys('testing') + inputbox.send_keys(Keys.ENTER) + self.wait_for_row_in_list_table('1: testing') + inputbox = self.browser.find_element_by_id('id_new_item') + self.assertAlmostEqual( + inputbox.location['x'] + inputbox.size['width'] / 2, + 512, + delta=10 + ) \ No newline at end of file diff --git a/functional_tests/test_list_item_validation.py b/functional_tests/test_list_item_validation.py new file mode 100644 index 0000000..ccf42f8 --- /dev/null +++ b/functional_tests/test_list_item_validation.py @@ -0,0 +1,24 @@ +from .base import FunctionalTest +from selenium.webdriver.common.keys import Keys +from unittest import skip + +class ItemValidationTest(FunctionalTest): + + def test_cannot_add_empty_list_items(self): + # Edith goes to the home page and accidentally tries to submit + # an empty list item. She hits Enter on the empty input box + + # The home page refreshes, and there is an error message saying + # that list items cannot be blank + + # She tries again with some text for the item, which now works + + # Perversely, she now decides to submit a second blank list item + + # She receives a similar warning on the list page + + # And she can correct it by filling some text in + self.fail('write me!') + +#if __name__ == '__main__': +# unittest.main(warnings='ignore') diff --git a/functional_tests/tests.py b/functional_tests/test_simple_list_creation.py similarity index 65% rename from functional_tests/tests.py rename to functional_tests/test_simple_list_creation.py index 32719c0..5bbaa02 100644 --- a/functional_tests/tests.py +++ b/functional_tests/test_simple_list_creation.py @@ -1,29 +1,10 @@ -from selenium.common.exceptions import WebDriverException -from django.contrib.staticfiles.testing import StaticLiveServerTestCase +from .base import FunctionalTest from selenium import webdriver from selenium.webdriver.common.keys import Keys from selenium.webdriver.firefox.options import Options -import time -import unittest -import environ -MAX_WAIT = 10 -class NewVisitorTest(StaticLiveServerTestCase): - - def setUp(self): - options = Options() - options.headless = True - self.browser = webdriver.Firefox(options=options) - self.browser.implicitly_wait(5) - - def tearDown(self): - self.browser.quit() - - def check_for_row_in_list_table(self, row_text): - 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]) +class NewVisitorTest(FunctionalTest): def test_can_start_a_list_for_one_user(self) : # Edith has heard about a cool new online to-do app. She goes @@ -73,19 +54,6 @@ class NewVisitorTest(StaticLiveServerTestCase): # She visits that URL - her to-do list is still there. # Satisfied, she goes back to sleep - 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) - def test_multiple_users_can_start_lists_at_different_urls(self): # Edith starts a new to-do list self.browser.get(self.live_server_url) @@ -131,31 +99,4 @@ class NewVisitorTest(StaticLiveServerTestCase): self.assertNotIn('Buy peacock feathers', page_text) self.assertIn('Buy milk', page_text) - # Satisfied, they both go back to sleep - - def test_layout_and_styling(self): - # Edith goes to the home page - self.browser.get(self.live_server_url) - self.browser.set_window_size(1024, 768) - - # She notices the input box is nicely centered - inputbox = self.browser.find_element_by_id('id_new_item') - self.assertAlmostEqual( - inputbox.location['x'] + inputbox.size['width'] / 2, - 512, - delta=10 - ) - # She starts a new list and sees the input is nicely - # centered there too - inputbox.send_keys('testing') - inputbox.send_keys(Keys.ENTER) - self.wait_for_row_in_list_table('1: testing') - inputbox = self.browser.find_element_by_id('id_new_item') - self.assertAlmostEqual( - inputbox.location['x'] + inputbox.size['width'] / 2, - 512, - delta=10 - ) - -#if __name__ == '__main__': -# unittest.main(warnings='ignore') + # Satisfied, they both go back to sleep \ No newline at end of file -- GitLab From ff945a8dafafdad43ccdc33b4e090254d3621de2 Mon Sep 17 00:00:00 2001 From: Farah Alhaniy Date: Mon, 14 Oct 2019 13:46:17 +0700 Subject: [PATCH 2/4] Split out unit tests into two files --- functional_tests/base.py | 12 ++++++- functional_tests/test_list_item_validation.py | 19 ++++++++++- lists/tests/__init__.py | 0 lists/tests/test_models.py | 32 +++++++++++++++++++ lists/{tests.py => tests/test_views.py} | 29 ----------------- 5 files changed, 61 insertions(+), 31 deletions(-) create mode 100644 lists/tests/__init__.py create mode 100644 lists/tests/test_models.py rename lists/{tests.py => tests/test_views.py} (83%) diff --git a/functional_tests/base.py b/functional_tests/base.py index 88b97f1..2550a0d 100644 --- a/functional_tests/base.py +++ b/functional_tests/base.py @@ -34,4 +34,14 @@ class FunctionalTest(StaticLiveServerTestCase): def check_for_row_in_list_table(self, row_text): 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]) \ No newline at end of file + self.assertIn(row_text, [row.text for row in rows]) + + def wait_for(self, fn): + start_time = time.time() + while True: + try: + return fn() + except (AssertionError, WebDriverException) as e: + if time.time() - start_time > MAX_WAIT: + raise e + time.sleep(0.5) \ No newline at end of file diff --git a/functional_tests/test_list_item_validation.py b/functional_tests/test_list_item_validation.py index ccf42f8..2f1c790 100644 --- a/functional_tests/test_list_item_validation.py +++ b/functional_tests/test_list_item_validation.py @@ -7,18 +7,35 @@ class ItemValidationTest(FunctionalTest): def test_cannot_add_empty_list_items(self): # Edith goes to the home page and accidentally tries to submit # an empty list item. She hits Enter on the empty input box + self.browser.get(self.live_server_url) + self.browser.find_element_by_id('id_new_item').send_keys(Keys.ENTER) # The home page refreshes, and there is an error message saying # that list items cannot be blank + self.wait_for(lambda: self.assertEqual( + self.browser.find_element_by_css_selector('.has-error').text, + "You can't have an empty list item" + ​)) # She tries again with some text for the item, which now works + self.browser.find_element_by_id('id_new_item').send_keys('Buy milk') + self.browser.find_element_by_id('id_new_item').send_keys(Keys.ENTER) + self.wait_for_row_in_list_table('1: Buy milk') # Perversely, she now decides to submit a second blank list item + self.browser.find_element_by_id('id_new_item').send_keys(Keys.ENTER) # She receives a similar warning on the list page + self.wait_for(lambda: self.assertEqual( + self.browser.find_element_by_css_selector('.has-error').text, + "You can't have an empty list item" + )) # And she can correct it by filling some text in - self.fail('write me!') + self.browser.find_element_by_id('id_new_item').send_keys('Make tea') + self.browser.find_element_by_id('id_new_item').send_keys(Keys.ENTER) + self.wait_for_row_in_list_table('1: Buy milk') + self.wait_for_row_in_list_table('2: Make tea') #if __name__ == '__main__': # unittest.main(warnings='ignore') diff --git a/lists/tests/__init__.py b/lists/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/lists/tests/test_models.py b/lists/tests/test_models.py new file mode 100644 index 0000000..75cca5d --- /dev/null +++ b/lists/tests/test_models.py @@ -0,0 +1,32 @@ +from django.test import TestCase +from lists.models import Item, List + + +class ListAndItemModelsTest(TestCase): + + def test_saving_and_retrieving_items(self): + list_ = List() + list_.save() + + first_item = Item() + first_item.text = 'The first (ever) list item' + first_item.list = list_ + first_item.save() + + second_item = Item() + second_item.text = 'Item the second' + second_item.list = list_ + second_item.save() + + saved_list = List.objects.first() + self.assertEqual(saved_list, list_) + + saved_items = Item.objects.all() + self.assertEqual(saved_items.count(), 2) + + first_saved_item = saved_items[0] + second_saved_item = saved_items[1] + self.assertEqual(first_saved_item.text, 'The first (ever) list item') + self.assertEqual(first_saved_item.list, list_) + self.assertEqual(second_saved_item.text, 'Item the second') + self.assertEqual(second_saved_item.list, list_) \ No newline at end of file diff --git a/lists/tests.py b/lists/tests/test_views.py similarity index 83% rename from lists/tests.py rename to lists/tests/test_views.py index 14daa5e..0225175 100644 --- a/lists/tests.py +++ b/lists/tests/test_views.py @@ -127,33 +127,4 @@ class HomePageTest(TestCase): response = self.client.get(f'/lists/{list_.id}/') self.assertIn('Oh tidak', response.content.decode()) -class ItemModelTest(TestCase): - - def test_saving_and_retrieving_items(self): - list_ = List() - list_.save() - - first_item = Item() - first_item.text = 'The first (ever) list item' - first_item.list = list_ - first_item.save() - - second_item = Item() - second_item.text = 'Item the second' - second_item.list = list_ - second_item.save() - - saved_list = List.objects.first() - self.assertEqual(saved_list, list_) - - saved_items = Item.objects.all() - self.assertEqual(saved_items.count(), 2) - - first_saved_item = saved_items[0] - second_saved_item = saved_items[1] - self.assertEqual(first_saved_item.text, 'The first (ever) list item') - self.assertEqual(first_saved_item.list, list_) - self.assertEqual(second_saved_item.text, 'Item the second') - self.assertEqual(second_saved_item.list, list_) - -- GitLab From ff37489fea47d7161594e0ef0fe22d698c5e2dee Mon Sep 17 00:00:00 2001 From: Farah Alhaniy Date: Mon, 14 Oct 2019 13:57:18 +0700 Subject: [PATCH 3/4] =?UTF-8?q?Latihan=205=20=E2=80=93=20Input=20Validatio?= =?UTF-8?q?n=20dan=20Test=20Organization?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/README.md b/README.md index ee58f77..1d12439 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,17 @@ # 1606821886-practice Herokuapp : https://pmpl-farah.herokuapp.com/ +## Table of Contents + + - Exercise 3 : + - Proses test isolation + - Perbedaan pada design + - Exercise 4 : + - Keterhubungan Chapter 8 dan 7 + - Exercise 5 : + - Keterkaitan refactoring dan clean code + - Keuntungan test organization. + ## Exercise 3 **Proses test isolation** -- GitLab From 80e814a5e9db92ed65bd04cc546be11d1f20db4f Mon Sep 17 00:00:00 2001 From: Farah Alhaniy Date: Mon, 14 Oct 2019 17:39:43 +0700 Subject: [PATCH 4/4] =?UTF-8?q?Latihan=205=20=E2=80=93=20Input=20Validatio?= =?UTF-8?q?n=20dan=20Test=20Organization=20(Add=20@skip=20and=20read=20me)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 15 ++++++++++++++- functional_tests/test_list_item_validation.py | 7 ++++--- functional_tests/test_simple_list_creation.py | 1 + 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 1d12439..70feba0 100644 --- a/README.md +++ b/README.md @@ -59,4 +59,17 @@ def test_can_start_a_list_and_retrieve_it_later(self): **Keterhubungan Chapter 8 dan 7** Pada bab 7, saya melakukan perubahan pada struktur kode yang memungkinkan ada lebih dari satu user yang bisa memulai sebuah To-Do List. Sehingga saya mengimplementasikan URL yang unik untuk setiap list, seperti petunjuk di buku. -Pada bab 8, saya memperbaiki tampilan halaman dengan menggunakan css dan Bootstrap. Dari html yang sudah dibuat pada bab 7, saya mengimplementasikan django template inheritance dengan membuat satu html sebagai "superclass" yaitu base.html. \ No newline at end of file +Pada bab 8, saya memperbaiki tampilan halaman dengan menggunakan css dan Bootstrap. Dari html yang sudah dibuat pada bab 7, saya mengimplementasikan django template inheritance dengan membuat satu html sebagai "superclass" yaitu base.html. + +## Exercise 5 +**Keterkaitan refactoring dan konsep clean code** +Tujuan utama dilakukannya refactoring adalah untuk mencapai atau memelihara clean code. Dengan Red Green Refactor, kita dapat : + - Menghindari duplication + - Meningkatkan code coverage + - Memudahkan maintenance + +Dengan menerapkan Red Green Refactor, berarti kita juga menerapkan konsep clean code. + +**Keuntungan test organization** +Memudahkan maintenance. Kita dapat dengan mudah menemukan test code yang kita butuhkan karena test sudah teratur dalam kategori-kategori. + diff --git a/functional_tests/test_list_item_validation.py b/functional_tests/test_list_item_validation.py index 2f1c790..2215c18 100644 --- a/functional_tests/test_list_item_validation.py +++ b/functional_tests/test_list_item_validation.py @@ -4,6 +4,7 @@ from unittest import skip class ItemValidationTest(FunctionalTest): + @skip def test_cannot_add_empty_list_items(self): # Edith goes to the home page and accidentally tries to submit # an empty list item. She hits Enter on the empty input box @@ -15,7 +16,7 @@ class ItemValidationTest(FunctionalTest): self.wait_for(lambda: self.assertEqual( self.browser.find_element_by_css_selector('.has-error').text, "You can't have an empty list item" - ​)) + )) # She tries again with some text for the item, which now works self.browser.find_element_by_id('id_new_item').send_keys('Buy milk') @@ -27,8 +28,8 @@ class ItemValidationTest(FunctionalTest): # She receives a similar warning on the list page self.wait_for(lambda: self.assertEqual( - self.browser.find_element_by_css_selector('.has-error').text, - "You can't have an empty list item" + self.browser.find_element_by_css_selector('.has-error').text, + "You can't have an empty list item" )) # And she can correct it by filling some text in diff --git a/functional_tests/test_simple_list_creation.py b/functional_tests/test_simple_list_creation.py index 5bbaa02..dfe3706 100644 --- a/functional_tests/test_simple_list_creation.py +++ b/functional_tests/test_simple_list_creation.py @@ -4,6 +4,7 @@ from selenium.webdriver.common.keys import Keys from selenium.webdriver.firefox.options import Options + class NewVisitorTest(FunctionalTest): def test_can_start_a_list_for_one_user(self) : -- GitLab