Fakultas Ilmu Komputer UI

Commit 925ea690 authored by Izzan Fakhril Islam's avatar Izzan Fakhril Islam
Browse files

Tutorial 9 PMPL

parent 0a9c2f40
## Repository Tutorial PMPL # Repository Tutorial PMPL
#### **Izzan Fakhril Islam (1606875806)** #### **Izzan Fakhril Islam (1606875806)**
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
6. Tutorial 6: Mutation Testing 6. Tutorial 6: Mutation Testing
7. Tutorial 7: Spiking & De-Spiking 7. Tutorial 7: Spiking & De-Spiking
8. Tutorial 8: Using Mocks 8. Tutorial 8: Using Mocks
9. Tutorial 9: Test Fixtures and a Decorator
**URL Heroku:** https://pmpl-izzan.herokuapp.com/ **URL Heroku:** https://pmpl-izzan.herokuapp.com/
...@@ -723,3 +724,33 @@ def test_send_mail_to_address_from_post(self, mock_send_mail): ...@@ -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:** 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`.
...@@ -27,6 +27,20 @@ class FunctionalTest(StaticLiveServerTestCase): ...@@ -27,6 +27,20 @@ class FunctionalTest(StaticLiveServerTestCase):
self.selenium.quit() self.selenium.quit()
super(FunctionalTest, self).tearDown() 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): def wait_for_row_list_in_table(self, row_text, table_id):
start_time = time.time() start_time = time.time()
selenium = self.selenium selenium = self.selenium
...@@ -42,17 +56,28 @@ class FunctionalTest(StaticLiveServerTestCase): ...@@ -42,17 +56,28 @@ class FunctionalTest(StaticLiveServerTestCase):
time.sleep(0.5) time.sleep(0.5)
# Same as wait_for_row_list_in_table but using lambda expressions # Same as wait_for_row_list_in_table but using lambda expressions
@wait
def wait_for(self, fn): def wait_for(self, fn):
start_time = time.time() return fn()
while True:
try:
return fn()
except (AssertionError, WebDriverException) as e:
if time.time() - start_time > self.MAX_WAIT:
raise e
time.sleep(0.5)
def get_host_from_selenium(self, url): def get_host_from_selenium(self, url):
selenium = self.selenium selenium = self.selenium
selenium.get(url) selenium.get(url)
return selenium return selenium
@wait
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)
@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)
...@@ -43,3 +43,7 @@ class LoginTest(FunctionalTest): ...@@ -43,3 +43,7 @@ class LoginTest(FunctionalTest):
lambda: logged_in_account.find_element_by_link_text('Log out') 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.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)
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)
...@@ -8,4 +8,5 @@ urlpatterns = [ ...@@ -8,4 +8,5 @@ urlpatterns = [
url(r'^send_email$', send_login_email, name='send_login_email'), url(r'^send_email$', send_login_email, name='send_login_email'),
url(r'^login$', login, name='login'), url(r'^login$', login, name='login'),
url(r'^logout$', logout, name='logout') url(r'^logout$', logout, name='logout')
] ]
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