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