From 8ed3892d0f83c79c3a7634bd2ebe0df07332248f Mon Sep 17 00:00:00 2001 From: Izzan Fakhril Islam Date: Wed, 27 Nov 2019 23:56:27 +0700 Subject: [PATCH 1/5] adding precreate session method --- tutorial/settings.py | 1 + tutorial_7/functional_tests/base.py | 14 ++++++++++ tutorial_7/functional_tests/test_login.py | 4 +++ tutorial_7/functional_tests/test_todo.py | 31 +++++++++++++++++++++++ tutorial_7/urls.py | 1 + tutorial_9/__init__.py | 0 tutorial_9/admin.py | 3 +++ tutorial_9/apps.py | 5 ++++ tutorial_9/migrations/__init__.py | 0 tutorial_9/models.py | 3 +++ tutorial_9/tests.py | 3 +++ tutorial_9/views.py | 3 +++ 12 files changed, 68 insertions(+) create mode 100644 tutorial_7/functional_tests/test_todo.py create mode 100644 tutorial_9/__init__.py create mode 100644 tutorial_9/admin.py create mode 100644 tutorial_9/apps.py create mode 100644 tutorial_9/migrations/__init__.py create mode 100644 tutorial_9/models.py create mode 100644 tutorial_9/tests.py create mode 100644 tutorial_9/views.py diff --git a/tutorial/settings.py b/tutorial/settings.py index 62e3054..c0bf4cc 100644 --- a/tutorial/settings.py +++ b/tutorial/settings.py @@ -52,6 +52,7 @@ INSTALLED_APPS = [ 'tutorial_5', 'tutorial_7', 'tutorial_8', + 'tutorial_9', ] AUTH_USER_MODEL = 'tutorial_7.User' diff --git a/tutorial_7/functional_tests/base.py b/tutorial_7/functional_tests/base.py index f507b3c..29346fb 100644 --- a/tutorial_7/functional_tests/base.py +++ b/tutorial_7/functional_tests/base.py @@ -56,3 +56,17 @@ class FunctionalTest(StaticLiveServerTestCase): selenium = self.selenium selenium.get(url) return selenium + + def wait_to_be_logged_in(self, email): + self.wait_for( + lambda: self.selenium.find_element_by_link_text('Log out') + ) + body = self.selenium.find_element_by_tag_name('body') + self.assertIn(email, body.text) + + def wait_to_be_logged_out(self, email): + self.wait_for( + lambda: self.selenium.find_element_by_name('email') + ) + body = self.selenium.find_element_by_tag_name('body') + self.assertNotIn(email, body.text) diff --git a/tutorial_7/functional_tests/test_login.py b/tutorial_7/functional_tests/test_login.py index 8628682..f5c7b83 100644 --- a/tutorial_7/functional_tests/test_login.py +++ b/tutorial_7/functional_tests/test_login.py @@ -43,3 +43,7 @@ class LoginTest(FunctionalTest): lambda: logged_in_account.find_element_by_link_text('Log out') ) self.assertIn(TEST_EMAIL, selenium_host.find_element_by_tag_name('body').text) + + self.wait_to_be_logged_in(email=TEST_EMAIL) + self.selenium.find_element_by_link_text('Log out').click() + self.wait_to_be_logged_out(email=TEST_EMAIL) diff --git a/tutorial_7/functional_tests/test_todo.py b/tutorial_7/functional_tests/test_todo.py new file mode 100644 index 0000000..7c02d4f --- /dev/null +++ b/tutorial_7/functional_tests/test_todo.py @@ -0,0 +1,31 @@ +from django.conf import settings +from django.contrib.auth import BACKEND_SESSION_KEY, SESSION_KEY, get_user_model +from django.contrib.sessions.backends.db import SessionStore +from .base import FunctionalTest +User = get_user_model() + + +class TodoListsTest(FunctionalTest): + + def create_pre_authenticated_session(self, email): + user = User.objects.create(email=email) + session = SessionStore() + session[SESSION_KEY] = user.pk + session[BACKEND_SESSION_KEY] = settings.AUTHENTICATION_BACKENDS[0] + session.save() + + self.selenium.get(self.live_server_url + '/tutorial-7/') + self.selenium.add_cookie(dict( + name=settings.SESSION_COOKIE_NAME, + value=session.session_key, + path='/tutorial-7/', + )) + + def test_logged_in_users_lists_are_saved_as_todo_lists(self): + email = 'rvd.cena@gmail.com' + self.selenium.get(self.live_server_url + '/tutorial-7/') + self.wait_to_be_logged_out(email) + + self.create_pre_authenticated_session(email) + self.selenium.get(self.live_server_url + '/tutorial-7/') + self.wait_to_be_logged_in(email) diff --git a/tutorial_7/urls.py b/tutorial_7/urls.py index 18fb1f1..9b561b4 100644 --- a/tutorial_7/urls.py +++ b/tutorial_7/urls.py @@ -8,4 +8,5 @@ urlpatterns = [ url(r'^send_email$', send_login_email, name='send_login_email'), url(r'^login$', login, name='login'), url(r'^logout$', logout, name='logout') + ] diff --git a/tutorial_9/__init__.py b/tutorial_9/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tutorial_9/admin.py b/tutorial_9/admin.py new file mode 100644 index 0000000..8c38f3f --- /dev/null +++ b/tutorial_9/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/tutorial_9/apps.py b/tutorial_9/apps.py new file mode 100644 index 0000000..b97612d --- /dev/null +++ b/tutorial_9/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class Tutorial9Config(AppConfig): + name = 'tutorial_9' diff --git a/tutorial_9/migrations/__init__.py b/tutorial_9/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tutorial_9/models.py b/tutorial_9/models.py new file mode 100644 index 0000000..71a8362 --- /dev/null +++ b/tutorial_9/models.py @@ -0,0 +1,3 @@ +from django.db import models + +# Create your models here. diff --git a/tutorial_9/tests.py b/tutorial_9/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/tutorial_9/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/tutorial_9/views.py b/tutorial_9/views.py new file mode 100644 index 0000000..91ea44a --- /dev/null +++ b/tutorial_9/views.py @@ -0,0 +1,3 @@ +from django.shortcuts import render + +# Create your views here. -- GitLab From dfbed10cfc755e7382c59bb1dade3f8cbca85c6f Mon Sep 17 00:00:00 2001 From: Izzan Fakhril Islam Date: Thu, 28 Nov 2019 00:34:03 +0700 Subject: [PATCH 2/5] create wait decorator --- tutorial_7/functional_tests/base.py | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/tutorial_7/functional_tests/base.py b/tutorial_7/functional_tests/base.py index 29346fb..52b7edb 100644 --- a/tutorial_7/functional_tests/base.py +++ b/tutorial_7/functional_tests/base.py @@ -27,6 +27,20 @@ class FunctionalTest(StaticLiveServerTestCase): self.selenium.quit() super(FunctionalTest, self).tearDown() + def wait(fn): + def modified_fn(*args, **kwargs): + start_time = time.time() + MAX_WAIT = 10 + while True: + try: + return fn(*args, **kwargs) + except (AssertionError, WebDriverException) as e: + if time.time() - start_time > MAX_WAIT: + raise e + time.sleep(0.5) + return modified_fn + + @wait def wait_for_row_list_in_table(self, row_text, table_id): start_time = time.time() selenium = self.selenium @@ -42,21 +56,16 @@ class FunctionalTest(StaticLiveServerTestCase): time.sleep(0.5) # Same as wait_for_row_list_in_table but using lambda expressions + @wait def wait_for(self, fn): - start_time = time.time() - while True: - try: - return fn() - except (AssertionError, WebDriverException) as e: - if time.time() - start_time > self.MAX_WAIT: - raise e - time.sleep(0.5) + return fn() def get_host_from_selenium(self, url): selenium = self.selenium selenium.get(url) return selenium + @wait def wait_to_be_logged_in(self, email): self.wait_for( lambda: self.selenium.find_element_by_link_text('Log out') @@ -64,9 +73,11 @@ class FunctionalTest(StaticLiveServerTestCase): body = self.selenium.find_element_by_tag_name('body') self.assertIn(email, body.text) + @wait def wait_to_be_logged_out(self, email): self.wait_for( lambda: self.selenium.find_element_by_name('email') ) body = self.selenium.find_element_by_tag_name('body') self.assertNotIn(email, body.text) + -- GitLab From cd8b363e4c863d02695eb17b3d798b03ed1de76d Mon Sep 17 00:00:00 2001 From: Izzan Fakhril Islam Date: Thu, 28 Nov 2019 00:34:43 +0700 Subject: [PATCH 3/5] removing unnecessary files --- tutorial/settings.py | 1 - tutorial_9/__init__.py | 0 tutorial_9/admin.py | 3 --- tutorial_9/apps.py | 5 ----- tutorial_9/migrations/__init__.py | 0 tutorial_9/models.py | 3 --- tutorial_9/tests.py | 3 --- tutorial_9/views.py | 3 --- 8 files changed, 18 deletions(-) delete mode 100644 tutorial_9/__init__.py delete mode 100644 tutorial_9/admin.py delete mode 100644 tutorial_9/apps.py delete mode 100644 tutorial_9/migrations/__init__.py delete mode 100644 tutorial_9/models.py delete mode 100644 tutorial_9/tests.py delete mode 100644 tutorial_9/views.py diff --git a/tutorial/settings.py b/tutorial/settings.py index c0bf4cc..62e3054 100644 --- a/tutorial/settings.py +++ b/tutorial/settings.py @@ -52,7 +52,6 @@ INSTALLED_APPS = [ 'tutorial_5', 'tutorial_7', 'tutorial_8', - 'tutorial_9', ] AUTH_USER_MODEL = 'tutorial_7.User' diff --git a/tutorial_9/__init__.py b/tutorial_9/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/tutorial_9/admin.py b/tutorial_9/admin.py deleted file mode 100644 index 8c38f3f..0000000 --- a/tutorial_9/admin.py +++ /dev/null @@ -1,3 +0,0 @@ -from django.contrib import admin - -# Register your models here. diff --git a/tutorial_9/apps.py b/tutorial_9/apps.py deleted file mode 100644 index b97612d..0000000 --- a/tutorial_9/apps.py +++ /dev/null @@ -1,5 +0,0 @@ -from django.apps import AppConfig - - -class Tutorial9Config(AppConfig): - name = 'tutorial_9' diff --git a/tutorial_9/migrations/__init__.py b/tutorial_9/migrations/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/tutorial_9/models.py b/tutorial_9/models.py deleted file mode 100644 index 71a8362..0000000 --- a/tutorial_9/models.py +++ /dev/null @@ -1,3 +0,0 @@ -from django.db import models - -# Create your models here. diff --git a/tutorial_9/tests.py b/tutorial_9/tests.py deleted file mode 100644 index 7ce503c..0000000 --- a/tutorial_9/tests.py +++ /dev/null @@ -1,3 +0,0 @@ -from django.test import TestCase - -# Create your tests here. diff --git a/tutorial_9/views.py b/tutorial_9/views.py deleted file mode 100644 index 91ea44a..0000000 --- a/tutorial_9/views.py +++ /dev/null @@ -1,3 +0,0 @@ -from django.shortcuts import render - -# Create your views here. -- GitLab From 7ebcab73d5e022355daa74dbc1e4a31d9e83af6b Mon Sep 17 00:00:00 2001 From: Izzan Fakhril Islam Date: Thu, 28 Nov 2019 01:01:35 +0700 Subject: [PATCH 4/5] updating README.md --- README.md | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/README.md b/README.md index edbc52b..e27e460 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ 6. Tutorial 6: Mutation Testing 7. Tutorial 7: Spiking & De-Spiking 8. Tutorial 8: Using Mocks +9. Tutorial 9: Test Fixtures and a Decorator **URL Heroku:** https://pmpl-izzan.herokuapp.com/ @@ -723,3 +724,33 @@ def test_send_mail_to_address_from_post(self, mock_send_mail): **Penjelasan:** pada potongan kode diatas, terdapat *decorator* yang diletakkan diatas *test case method*, yaitu `@patch('tutorial_7.views.send_mail')`, dimana *decorator* tersebut menandakan bahwa *test case method* dibawahnya akan melakukan *mocking* terhadap *method* `send_mail` yang terdapat di file `views.py` milik modul `tutorial_7`. Selanjutnya, *method* yang sudah dilakukan *mocking* tersebut disimpan dalam sebuah parameter yang bernama `mock_send_mail`. Dan dilakukan pengecekan apakah *method* tersebut terpanggil melalui POST request, dan pencocokan argumen-argumen yang diterima oleh *method* yang sudah dilakukan *mocking* tersebut. +## Penjelasan Tutorial 9 + +Berdasarkan buku **Test-Driven Development with Python 2nd Edition, bab 20.1,** terdapat beberapa penjelasan mengenai *user authentication*, dimana dalam hal ini membuat *functional test* yang berfungsi untuk melakukan *testing* terhadap alur *login* terhadap *user* yang sudah terdaftar. + +Sebenarnya, hal ini sudah pernah dilakukan dalam pengerjaan **Tutorial 7**, atau lebih tepatnya dalam **buku acuan bab 18.3**. Namun, terdapat beberapa perbedaan dalam implementasi tutorial ini dengan tutorial sebelumnya, yaitu: + +- Pada Tutorial 7, dilakukan *functional testing* untuk alur *login* dengan memasukkan email secara manual kedalam *form* yang sudah dibuat di file HTML-nya, dan mengecek inbox untuk memastikan apakah telah dikirim email yang berisikan `uid` yang sudah dibuat. Alur berjalannya *functional testing* pada Tutorial 7 secara garis besar terbagi menjadi 3 bagian, yaitu **Pengisian form email, Pengecekan mailbox,** dan **Pengecekan link `uid` yang diberikan**. + +- Pada Tutorial 9, dilakukan *functional testing* untuk alur *login* dengan membuat sebuah objek ***session*** baru yang berisikan informasi-informasi yang digunakan untuk *login*, yaitu email pengguna. Pembuatan objek *session* ini berlangsung secara *internal* di dalam modul `tutorial_7`, tanpa melibatkan interaksi dengan modul luar seperti *mailbox*. Berikut adalah potongan kode yang menjalankan fungsi tersebut. + + ```python + def create_pre_authenticated_session(self, email): + user = User.objects.create(email=email) + session = SessionStore() + session[SESSION_KEY] = user.pk + session[BACKEND_SESSION_KEY] = settings.AUTHENTICATION_BACKENDS[0] + session.save() + + self.selenium.get(self.live_server_url + '/tutorial-7/') + self.selenium.add_cookie(dict( + name=settings.SESSION_COOKIE_NAME, + value=session.session_key, + path='/tutorial-7/', + )) + ``` + + **Penjelasan:** pada potongan kode diatas, dibentuk sebuah *instance* dari model `User` dengan *primary key* email yang menjadi parameter dari *method* tersebut. Selanjutnya, digunakan sebuah *library* bawaan Django yaitu `django.contrib.auth` yang menyediakan penyimpanan informasi *session*, dan dilakukan *assignment* untuk variabel `SESSION_KEY` dengan menggunakan *primary key* dari *instance* model `User` yang telah dibuat (email), dan selanjutnya *session* yang telah dibuat di-*save*, dan menjadi penanda adanya sebuah user yang sedang aktif (*logged in*). + +Penggunaan *method* `create_pre_authenticated_session` ini memiliki beberapa kelebihan dibandingkan dengan implementasi pada Tutorial 7. Diantaranya, setiap *test case* tidak perlu melewati tahapan-tahapan mengirimkan email yang berisikan UID, yang mana memakan waktu dan tidak perlu melakukan interaksi dengan modul luar, cukup hanya di dalam modul `tutorial_7`. + -- GitLab From 755c4147fe6cc4155f517170d2cad36837f15eac Mon Sep 17 00:00:00 2001 From: Izzan Fakhril Islam Date: Thu, 28 Nov 2019 01:08:19 +0700 Subject: [PATCH 5/5] updating README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e27e460..ff76065 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -## Repository Tutorial PMPL +# Repository Tutorial PMPL #### **Izzan Fakhril Islam (1606875806)** -- GitLab