Fakultas Ilmu Komputer UI

Commit 36ef66b1 authored by Izzan Fakhril Islam's avatar Izzan Fakhril Islam
Browse files

Tutorial 4 PMPL

parent 722362f5
......@@ -123,3 +123,269 @@ def test_input_todo(self):
- Digunakan variabel `MAX_WAIT` sebagai penanda batas waktu untuk melakukan *looping* menunggu halaman selesai di-*load*. Variabel ini bernilai 10 yang berarti 10 detik.
- Diberikan potongan kode `while True` dimana Selenium akan terus mengecek apakah terdapat kata `'Dummy'` dalam daftar tabel yang ditampilkan.
- Diberikan *error handling* pada potongan kode `except (AssertionError, WebDriverException) as e` dimana dilakukan pengecekan apakah waktu yang berjalan sudah melebihi nilai `MAX_WAIT`. Jika sudah, maka kedua *exeption* tersebut akan di-*raise* dan test gagal. Sedangkan jika belum, akan dilakukan *sleep* selama 0.5 detik sebelum kembali melakukan pengecekan.
## Penjelasan Tutorial 4
Berdasarkan buku **Test-Driven Development with Python 2nd Edition,** tutorial ini memiliki beberapa keterkaitan dengan materi yang disajikan di bab 7 (**Working Incrementally**), diantaranya adalah sebagai berikut.
- Implementasi *method*-*method* pada *unit test* untuk pengecekan *redirection* dan penggunaan *template file* oleh pada berkas `views.py`. Diantaranya sebagai berikut.
```python
class Tutorial2UnitTest(TestCase):
def test_use_correct_template(self):
response = self.client.get('/tutorial-2/')
self.assertTemplateUsed(response, 'tutorial_2.html')
def test_can_save_POST_request_todo(self):
self.client.post(
'/tutorial-2/add_todo/',
data={
'date': '2019-09-05T14:31',
'activity': DUMMY_TODO_ITEM
}
)
todo_count = TodoList.objects.count()
new_todo = TodoList.objects.first()
self.assertEqual(todo_count, 1)
self.assertEqual(getattr(new_todo, 'todo_list'), DUMMY_TODO_ITEM)
def test_can_save_POST_request_todo_commentary(self):
self.client.post(
'/tutorial-2/add_todo_commentary/',
data={
'date': '2019-09-12',
'comment': DUMMY_TODO_COMMENTARY_ITEM
}
)
todo_commentary_count = TodoListCommentary.objects.count()
new_todo_commentary = TodoListCommentary.objects.first()
self.assertEqual(todo_commentary_count, 1)
self.assertEqual(getattr(new_todo_commentary, 'comment'), DUMMY_TODO_COMMENTARY_ITEM)
def test_redirects_after_POST_todo(self):
response = self.client.post(
'/tutorial-2/add_todo/',
data={
'date': '2019-09-05T14:31',
'activity': DUMMY_TODO_ITEM
}
)
self.assertEqual(response.status_code, 302)
self.assertRedirects(response, '/tutorial-2/')
def test_redirects_after_POST_todo_commentary(self):
response = self.client.post(
'/tutorial-2/add_todo_commentary/',
data={
'date': '2019-09-12',
'comment': DUMMY_TODO_COMMENTARY_ITEM
}
)
self.assertEqual(response.status_code, 302)
self.assertRedirects(response, '/tutorial-2/')
```
Selain itu, saya juga menambahkan implementasi *unit tests* dengan menggunakan `LiveServerTestCase` yaitu sebuah *library* bawaan dari Django, sehingga saya tidak perlu menjalankan perintah `runserver` lagi.
- Pengimplementasian *functional test* yang sudah disesuaikan dengan *unit test* yang diimplementasikan berdasarkan bab 7, sehingga ketika dijalankan *automated script* untuk *unit test* maupun *functional test*, berhasil.
Selanjutnya, untuk pengimplementasian tutorial 4 ini, saya menambahkan **aesthetics test**, yaitu *functional testing* pada CSS dengan menggunakan modul `StaticLiveServerTestCase` yang merupakan bawaan dari Django. Berikut adalah salah satu contoh implementasinya.
```python
def test_layout_and_styling_todo_input_textbox(self):
selenium = self.selenium
selenium.get(self.host)
selenium.set_window_size(1024, 768)
todo_date = selenium.find_element_by_id('todo_date')
self.assertAlmostEqual(
todo_date.location['x'] + todo_date.size['width'] / 2,
740,
delta=10
)
def test_layout_and_styling_todo_commentary_input_textbox(self):
selenium = self.selenium
selenium.get(self.host)
selenium.set_window_size(1024, 768)
todo_commentary = selenium.find_element_by_id('comment_date')
self.assertAlmostEqual(
todo_commentary.location['x'] + todo_commentary.size['width'] / 2,
780,
delta=10
)
```
Untuk *templating languange* pada HTML, saya sudah mengimplementasikan sebelumya, dimulai dari *app* `tutorial-1` dan `tutorial-2`
````html
{% load staticfiles %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="description" content="Tutorial 2 PMPL">
<meta name="author" content="{{ author }}">
<!-- bootstrap csss -->
<link href="//netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css" rel="stylesheet">
<title>
{% block title %} Tutorial 2 PMPL by {{ author }} - {{ npm }}{% endblock %}
</title>
</head>
<style>
@import url(https://fonts.googleapis.com/css?family=Roboto|Roboto+Slab);
.container, .footer-down, .header-up{
font-family: "Roboto", "sans-serif";
font-weight: normal;
}
</style>
<body style="background-color: darkseagreen">
<header class="header-up">
{% include "tutorial_2/../partials/header.html" %}
</header>
<content>
<div class="container">
{% block content %}
{% endblock %}
</div>
</content>
<footer class="footer-down">
<!-- TODO Block Footer dan include footer.html -->
{% block footer %}
{% include "tutorial_2/../partials/footer.html" %}
{% endblock %}
</footer>
<!-- Jquery n Bootstrap Script -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script type="application/javascript" src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
</body>
</html>
````
Dan berikut adalah konten dari `tutorial_2.html` saya.
```html
{% extends "tutorial_2/../layout/base.html" %}
{% block content %}
<style>
.form-control {
@import url(https://fonts.googleapis.com/css?family=Droid+Sans+Mono);
margin-top: 3px;
margin-bottom: 3px;
font-family: "Droid Sans Mono";
}
table {
text-align: left;
padding: 8px;
width: 100%;
}
</style>
<h1 align="center" style="font-weight: bold">{{ author }}'s Todo List</h1>
<br><br>
<div class="row">
<div class="col-md-6 col-xs-6">
<h4>Todo List Table</h4>
<br>
<table id="todo_table">
<tr>
<th><h5><b>Date/Time</b></h5></th>
<th><h5><b>Todo</b></h5></th>
</tr>
{% for todos in todos_dict %}
<tr>
<td>{{ todos.date }}</td>
<td>{{ todos.todo_list }}</td>
</tr>
{% endfor %}
</table>
<br>
<h4>Daily Todo List Comments</h4>
<br>
<table id="todo_comment_table">
<tr>
<th><h5><b>Date</b></h5></th>
<th><h5><b>Comment</b></h5></th>
</tr>
{% for commentary in todos_commentary_dict %}
<tr>
<td>{{ commentary.date }}</td>
<td>{{ commentary.comment }}</td>
</tr>
{% endfor %}
</table>
<br>
</div>
<div class="col-md-6 col-xs-6">
<h4><b>Add Todo</b></h4>
<form method="POST" action="{% url 'tutorial-2:add_todo' %}">
{% csrf_token %}
<table>
<tr>
<td>
Date/Time
</td>
<td>
<input type="datetime-local" name="date" id="todo_date" required="required" size="27"/>
</td>
</tr>
<tr>
<td>
Todo
</td>
<td>
<input type="text" name="activity" id="activity" required="required" size="27">
</td>
</tr>
</table>
<br>
<button type="submit" id="todo_submit" class="button btn-success">Submit</button>
</form>
<br>
<div class="errornote">
<p style="color: black; font-family: 'Roboto Slab'">{{ error_msg }}</p>
</div>
<h4><b>Add Daily Todo List Comment</b></h4>
<form method="POST" action="{% url 'tutorial-2:add_todo_commentary' %}">
{% csrf_token %}
<table>
<tr>
<td>
Date
</td>
<td>
<input type="date" name="date" id="comment_date" required="required" size="27"/>
</td>
</tr>
<tr>
<td>
Todo List Comment
</td>
<td>
<input type="text" name="comment" id="comment" required="required" size="27">
</td>
</tr>
</table>
<br>
<button type="submit" id="comment_submit" class="button btn-success">Submit</button>
</form>
<br>
<div class="errornote">
<p style="color: black; font-family: 'Roboto Slab'">{{ commentary_error_msg }}</p>
</div>
</div>
</div>
{% endblock %}
```
......@@ -48,6 +48,7 @@ INSTALLED_APPS = [
'tutorial_1',
'tutorial_2',
'tutorial_3',
'tutorial_4',
]
MIDDLEWARE = [
......
# Tutorial 2 PMPL Functional Test, using Django's LiveServerTestCase
import time
import unittest
from django.test import LiveServerTestCase
from selenium import webdriver
......@@ -9,6 +10,7 @@ from selenium.webdriver.common.keys import Keys
from selenium.common.exceptions import WebDriverException
@unittest.skip("excluded")
class Tutorial2FunctionalTest(LiveServerTestCase):
def setUp(self):
......
from django.apps import AppConfig
class Tutorial4Config(AppConfig):
name = "tutorial_4"
# Tutorial 4 Functional Test: Tutorial 2's module with StaticLiveServerTestCase module by Django
import time
from django.contrib.staticfiles.testing import StaticLiveServerTestCase
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.keys import Keys
from selenium.common.exceptions import WebDriverException
class Tutorial2FunctionalStaticfilesTest(StaticLiveServerTestCase):
def setUp(self):
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.selenium = webdriver.Chrome('./chromedriver', chrome_options=chrome_options)
self.MAX_WAIT = 10
self.host = self.live_server_url + "/tutorial-2/"
self.selenium.implicitly_wait(3)
super(Tutorial2FunctionalStaticfilesTest, self).setUp()
def tearDown(self):
self.selenium.quit()
super(Tutorial2FunctionalStaticfilesTest, self).tearDown()
def wait_for_row_list_in_table(self, row_text, table_id):
start_time = time.time()
selenium = self.selenium
while True:
try:
table = selenium.find_element_by_id(table_id)
rows = table.find_elements_by_tag_name("td")
self.assertIn(row_text, [row.text for row in rows])
return
except (AssertionError, WebDriverException) as e:
if time.time() - start_time > self.MAX_WAIT:
raise e
time.sleep(0.5)
def test_find_name(self):
selenium = self.selenium
selenium.get(self.host)
self.assertIn('Izzan Fakhril Islam', selenium.page_source)
def test_input_todo_item(self):
selenium = self.selenium
selenium.get(self.host)
todo_date = selenium.find_element_by_id('todo_date')
todo_date.send_keys('23052019')
todo_date.send_keys(Keys.TAB)
todo_date.send_keys('1245')
todo_text = selenium.find_element_by_id('activity')
todo_text.send_keys('Dummy')
todo_text.send_keys(Keys.ENTER)
time.sleep(1)
self.wait_for_row_list_in_table("Dummy", "todo_table")
def test_layout_and_styling_todo_input_textbox(self):
selenium = self.selenium
selenium.get(self.host)
selenium.set_window_size(1024, 768)
todo_date = selenium.find_element_by_id('todo_date')
self.assertAlmostEqual(
todo_date.location['x'] + todo_date.size['width'] / 2,
740,
delta=10
)
def test_layout_and_styling_todo_commentary_input_textbox(self):
selenium = self.selenium
selenium.get(self.host)
selenium.set_window_size(1024, 768)
todo_commentary = selenium.find_element_by_id('comment_date')
self.assertAlmostEqual(
todo_commentary.location['x'] + todo_commentary.size['width'] / 2,
780,
delta=10
)
from django.test import LiveServerTestCase, TestCase
from django.test import Client
from tutorial_2.views import index
from django.http import HttpRequest
from tutorial_2.models import TodoListCommentary, TodoList
DUMMY_TODO_ITEM = "Dummy todo item"
DUMMY_TODO_COMMENTARY_ITEM = "Dummy todo commentary item"
class Tutorial2UnitTest(TestCase):
def test_use_correct_template(self):
response = self.client.get('/tutorial-2/')
self.assertTemplateUsed(response, 'tutorial_2.html')
def test_can_save_POST_request_todo(self):
self.client.post(
'/tutorial-2/add_todo/',
data={
'date': '2019-09-05T14:31',
'activity': DUMMY_TODO_ITEM
}
)
todo_count = TodoList.objects.count()
new_todo = TodoList.objects.first()
self.assertEqual(todo_count, 1)
self.assertEqual(getattr(new_todo, 'todo_list'), DUMMY_TODO_ITEM)
def test_can_save_POST_request_todo_commentary(self):
self.client.post(
'/tutorial-2/add_todo_commentary/',
data={
'date': '2019-09-12',
'comment': DUMMY_TODO_COMMENTARY_ITEM
}
)
todo_commentary_count = TodoListCommentary.objects.count()
new_todo_commentary = TodoListCommentary.objects.first()
self.assertEqual(todo_commentary_count, 1)
self.assertEqual(getattr(new_todo_commentary, 'comment'), DUMMY_TODO_COMMENTARY_ITEM)
def test_redirects_after_POST_todo(self):
response = self.client.post(
'/tutorial-2/add_todo/',
data={
'date': '2019-09-05T14:31',
'activity': DUMMY_TODO_ITEM
}
)
self.assertEqual(response.status_code, 302)
self.assertRedirects(response, '/tutorial-2/')
def test_redirects_after_POST_todo_commentary(self):
response = self.client.post(
'/tutorial-2/add_todo_commentary/',
data={
'date': '2019-09-12',
'comment': DUMMY_TODO_COMMENTARY_ITEM
}
)
self.assertEqual(response.status_code, 302)
self.assertRedirects(response, '/tutorial-2/')
class Tutorial2LiveServerUnitTest(LiveServerTestCase):
def test_use_correct_template(self):
response = self.client.get(self.live_server_url + '/tutorial-2/')
self.assertTemplateUsed(response, 'tutorial_2.html')
def test_can_save_POST_request_todo(self):
self.client.post(
self.live_server_url + '/tutorial-2/add_todo/',
data={
'date': '2019-09-05T14:31',
'activity': DUMMY_TODO_ITEM
}
)
todo_count = TodoList.objects.count()
new_todo = TodoList.objects.first()
self.assertEqual(todo_count, 1)
self.assertEqual(getattr(new_todo, 'todo_list'), DUMMY_TODO_ITEM)
def test_can_save_POST_request_todo_commentary(self):
self.client.post(
self.live_server_url + '/tutorial-2/add_todo_commentary/',
data={
'date': '2019-09-12',
'comment': DUMMY_TODO_COMMENTARY_ITEM
}
)
todo_commentary_count = TodoListCommentary.objects.count()
new_todo_commentary = TodoListCommentary.objects.first()
self.assertEqual(todo_commentary_count, 1)
self.assertEqual(getattr(new_todo_commentary, 'comment'), DUMMY_TODO_COMMENTARY_ITEM)
def test_redirects_after_POST_todo(self):
response = self.client.post(
self.live_server_url + '/tutorial-2/add_todo/',
data={
'date': '2019-09-05T14:31',
'activity': DUMMY_TODO_ITEM
}
)
self.assertEqual(response.status_code, 302)
self.assertRedirects(response, '/tutorial-2/')
def test_redirects_after_POST_todo_commentary(self):
response = self.client.post(
self.live_server_url + '/tutorial-2/add_todo_commentary/',
data={
'date': '2019-09-12',
'comment': DUMMY_TODO_COMMENTARY_ITEM
}
)
self.assertEqual(response.status_code, 302)
self.assertRedirects(response, '/tutorial-2/')
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