diff --git a/web/pgadmin/conftest.py b/web/conftest.py
similarity index 88%
rename from web/pgadmin/conftest.py
rename to web/conftest.py
index 4ab174ed..c0c3476d 100644
--- a/web/pgadmin/conftest.py
+++ b/web/conftest.py
@@ -6,17 +6,39 @@
 # This software is released under the PostgreSQL Licence
 #
 ##########################################################################
+
+import atexit
+import logging
 import os
+import signal
 import sys
 
 import pytest
+from selenium import webdriver
+from selenium.webdriver.chrome.options import Options
+
+logger = logging.getLogger(__name__)
+file_name = os.path.basename(__file__)
+
+if sys.version_info < (2, 7):
+    pass
+else:
+    pass
+
+if sys.version_info[0] >= 3:
+    import builtins
+else:
+    import __builtin__ as builtins
+
+# Ensure the global server mode is set.
+builtins.SERVER_MODE = None
 
 logger = logging.getLogger(__name__)
 file_name = os.path.basename(__file__)
 
 CURRENT_PATH = os.path.dirname(os.path.realpath(__file__))
 
-root = os.path.dirname(CURRENT_PATH)
+root = os.path.dirname(CURRENT_PATH) + os.path.sep + 'web'
 
 if sys.path[0] != root:
     sys.path.insert(0, root)
@@ -36,6 +58,13 @@ from regression import test_setup
 
 from regression.feature_utils.app_starter import AppStarter
 
+if config.SERVER_MODE is True:
+    config.SECURITY_RECOVERABLE = True
+    config.SECURITY_CHANGEABLE = True
+    config.SECURITY_POST_CHANGE_VIEW = 'browser.change_password'
+
+from regression.feature_utils.app_starter import AppStarter
+
 # Delete SQLite db file if exists
 if os.path.isfile(config.TEST_SQLITE_PATH):
     os.remove(config.TEST_SQLITE_PATH)
@@ -74,6 +103,11 @@ from regression.python_test_utils import test_utils
 
 config.SETTINGS_SCHEMA_VERSION = SCHEMA_VERSION
 
+# Override some other defaults
+from logging import WARNING
+
+config.CONSOLE_LOG_LEVEL = WARNING
+
 # Create the app
 app = create_app()
 app.config['WTF_CSRF_ENABLED'] = False
@@ -87,14 +121,25 @@ handle_cleanup = None
 server_info = test_utils.get_config_data()
 
 
+@pytest.hookimpl(tryfirst=True, hookwrapper=True)
+def pytest_runtest_makereport(item, call):
+    # execute all other hooks to obtain the report object
+    outcome = yield
+    rep = outcome.get_result()
+
+    # set a report attribute for each phase of a call, which can
+    # be "setup", "call", "teardown"
+
+    setattr(item, "rep_" + rep.when, rep)
+
+
 @pytest.fixture(scope="session", autouse=True)
-def database_server():
+def database_server(request):
     server_information = test_utils.create_parent_server_node(server_info[0])
+    request.addfinalizer(lambda: test_utils.delete_test_server(test_client))
 
     yield server_information
 
-    test_utils.delete_test_server(test_client)
-
 
 @pytest.fixture(scope='session')
 def context_of_tests(database_server):
diff --git a/web/package.json b/web/package.json
index 0e895968..23ea3d0f 100644
--- a/web/package.json
+++ b/web/package.json
@@ -102,8 +102,8 @@
     "test:karma-once": "yarn run linter && yarn run karma start --single-run",
     "test:karma": "yarn run linter && yarn run karma start",
     "test:unit": "yarn run pep8 && python -m pytest -q pgadmin",
-    "test:feature": "yarn run bundle && python regression/runtests.py --pkg feature_tests",
-    "test": "yarn run test:karma-once && yarn run bundle && yarn test:unit && python regression/runtests.py --pkg feature_tests",
+    "test:feature": "yarn run bundle && python -m pytest -q regression/feature_tests",
+    "test": "yarn run test:karma-once && yarn run bundle && yarn test:unit && yarn test:feature",
     "pep8": "pycodestyle --config=.pycodestyle ."
   }
 }
diff --git a/web/pgadmin/browser/server_groups/servers/tests/test_add_server_with_ssh_tunnel.py b/web/pgadmin/browser/server_groups/servers/tests/test_add_server_with_ssh_tunnel.py
index 9cc4f6a4..d14b3002 100644
--- a/web/pgadmin/browser/server_groups/servers/tests/test_add_server_with_ssh_tunnel.py
+++ b/web/pgadmin/browser/server_groups/servers/tests/test_add_server_with_ssh_tunnel.py
@@ -6,12 +6,11 @@
 # This software is released under the PostgreSQL Licence
 #
 ##########################################################################
-
+import copy
 import json
 
 from grappa import should
 
-from pgadmin.utils.base_test_generator import BaseTestGenerator
 from regression.python_test_utils import test_utils as utils
 
 
@@ -29,7 +28,7 @@ class TestServersWithSSHTunnelAdd:
 
         url = "/browser/server/obj/{0}/".format(utils.SERVER_GROUP)
 
-        self.server = context_of_tests['server']
+        self.server = copy.deepcopy(context_of_tests['server'])
         self.tester = context_of_tests['test_client']
         self.server['use_ssh_tunnel'] = 1
         self.server['tunnel_host'] = '127.0.0.1'
@@ -61,7 +60,7 @@ class TestServersWithSSHTunnelAdd:
 
         url = "/browser/server/obj/{0}/".format(utils.SERVER_GROUP)
 
-        self.server = context_of_tests['server']
+        self.server = copy.deepcopy(context_of_tests['server'])
         self.tester = context_of_tests['test_client']
         self.server['use_ssh_tunnel'] = 1
         self.server['tunnel_host'] = '127.0.0.1'
diff --git a/web/regression/conftest.py b/web/regression/conftest.py
deleted file mode 100644
index 1f66b465..00000000
--- a/web/regression/conftest.py
+++ /dev/null
@@ -1,35 +0,0 @@
-#
-# pgAdmin 4 - PostgreSQL Tools
-#
-# Copyright (C) 2013 - 2018, The pgAdmin Development Team
-# This software is released under the PostgreSQL Licence
-#
-
-# def pytest_generate_tests(metafunc):
-#     print('\n\n\npytest_generate_tests\n\n\n')
-#     idlist = []
-#     argvalues = []
-#     for scenario in metafunc.cls.scenarios:
-#         idlist.append(scenario[0])
-#         items = scenario[1].items()
-#         argnames = [x[0] for x in items]
-#         argvalues.append(([x[1] for x in items]))
-#     metafunc.parametrize(argnames, argvalues, ids=idlist, scope="class")
-import sys
-
-
-def pytest_generate_tests(metafunc):
-    print('Generation next')
-    idlist = []
-    argvalues = []
-    argnames = []
-    print('output', file=sys.stderr)
-    for scenario in metafunc.cls.scenarios:
-        idlist.append(scenario[0])
-        items = scenario[1].items()
-        argnames = [x[0] for x in items]
-        argvalues.append(([x[1] for x in items]))
-        print('bamm', file=sys.stderr)
-    print('shebang', file=sys.stderr)
-    metafunc.parametrize(argnames, argvalues, ids=idlist, scope="class",
-                         indirect=False)
diff --git a/web/pgadmin/feature_tests/__init__.py b/web/regression/feature_tests/__init__.py
similarity index 100%
rename from web/pgadmin/feature_tests/__init__.py
rename to web/regression/feature_tests/__init__.py
diff --git a/web/pgadmin/feature_tests/copy_selected_query_results_feature_test.py b/web/regression/feature_tests/copy_selected_query_results_feature_test.py
similarity index 85%
rename from web/pgadmin/feature_tests/copy_selected_query_results_feature_test.py
rename to web/regression/feature_tests/copy_selected_query_results_feature_test.py
index ab54d289..9dbdb53d 100644
--- a/web/pgadmin/feature_tests/copy_selected_query_results_feature_test.py
+++ b/web/regression/feature_tests/copy_selected_query_results_feature_test.py
@@ -8,6 +8,7 @@
 ##########################################################################
 
 import pyperclip
+from grappa import should
 
 from selenium.webdriver import ActionChains
 from selenium.webdriver.common.keys import Keys
@@ -16,16 +17,15 @@ from regression.python_test_utils import test_utils
 from regression.feature_utils.base_feature_test import BaseFeatureTest
 
 
-class CopySelectedQueryResultsFeatureTest(BaseFeatureTest):
-    """
-    Tests various ways to copy data from the query results grid.
-    """
+class TestCopySelectedQueryResults(BaseFeatureTest):
+    def test_copy_selected_query_results(self, driver):
+        """
+        Tests various ways to copy data from the query results grid.
+        """
+        self.driver = driver
 
-    scenarios = [
-        ("Copy rows, column using button and keyboard shortcut", dict())
-    ]
+        self.setUp()
 
-    def before(self):
         connection = test_utils.get_db_connection(
             self.server['db'],
             self.server['username'],
@@ -40,7 +40,6 @@ class CopySelectedQueryResultsFeatureTest(BaseFeatureTest):
             self.server, "acceptance_test_db", "test_table")
         self.page.add_server(self.server)
 
-    def runTest(self):
         self.page.toggle_open_tree_item(self.server['name'])
         self.page.toggle_open_tree_item('Databases')
         self.page.toggle_open_tree_item('acceptance_test_db')
@@ -63,6 +62,19 @@ class CopySelectedQueryResultsFeatureTest(BaseFeatureTest):
         self._shift_resizes_column_selection()
         self._mouseup_outside_grid_still_makes_a_selection()
 
+        self.page.close_query_tool()
+        self.page.remove_server(self.server)
+
+        connection = test_utils.get_db_connection(
+            self.server['db'],
+            self.server['username'],
+            self.server['db_password'],
+            self.server['host'],
+            self.server['port'],
+            self.server['sslmode']
+        )
+        test_utils.drop_database(connection, "acceptance_test_db")
+
     def _copies_rows(self):
         pyperclip.copy("old clipboard contents")
         self.page.find_by_xpath(
@@ -70,8 +82,7 @@ class CopySelectedQueryResultsFeatureTest(BaseFeatureTest):
 
         self.page.find_by_xpath("//*[@id='btn-copy-row']").click()
 
-        self.assertEqual('"Some-Name"\t"6"\t"some info"',
-                         pyperclip.paste())
+        pyperclip.paste() | should.equal('"Some-Name"\t"6"\t"some info"')
 
     def _copies_columns(self):
         pyperclip.copy("old clipboard contents")
@@ -81,11 +92,9 @@ class CopySelectedQueryResultsFeatureTest(BaseFeatureTest):
         ).click()
         self.page.find_by_xpath("//*[@id='btn-copy-row']").click()
 
-        self.assertEqual(
-            """\"Some-Name"
+        pyperclip.paste() | should.equal("""\"Some-Name"
 "Some-Other-Name"
-"Yet-Another-Name\"""",
-            pyperclip.paste())
+"Yet-Another-Name\"""")
 
     def _copies_row_using_keyboard_shortcut(self):
         pyperclip.copy("old clipboard contents")
@@ -95,8 +104,7 @@ class CopySelectedQueryResultsFeatureTest(BaseFeatureTest):
         ActionChains(self.page.driver).key_down(
             Keys.CONTROL).send_keys('c').key_up(Keys.CONTROL).perform()
 
-        self.assertEqual('"Some-Name"\t"6"\t"some info"',
-                         pyperclip.paste())
+        pyperclip.paste() | should.equal('"Some-Name"\t"6"\t"some info"')
 
     def _copies_column_using_keyboard_shortcut(self):
         pyperclip.copy("old clipboard contents")
@@ -108,11 +116,9 @@ class CopySelectedQueryResultsFeatureTest(BaseFeatureTest):
         ActionChains(self.page.driver).key_down(
             Keys.CONTROL).send_keys('c').key_up(Keys.CONTROL).perform()
 
-        self.assertEqual(
-            """\"Some-Name"
+        pyperclip.paste() | should.equal("""\"Some-Name"
 "Some-Other-Name"
-"Yet-Another-Name\"""",
-            pyperclip.paste())
+"Yet-Another-Name\"""")
 
     def _copies_rectangular_selection(self):
         pyperclip.copy("old clipboard contents")
@@ -134,8 +140,8 @@ class CopySelectedQueryResultsFeatureTest(BaseFeatureTest):
             self.page.driver
         ).key_down(Keys.CONTROL).send_keys('c').key_up(Keys.CONTROL).perform()
 
-        self.assertEqual("""\"Some-Other-Name"\t"22"
-"Yet-Another-Name"\t"14\"""", pyperclip.paste())
+        pyperclip.paste() | should.equal("""\"Some-Other-Name"\t"22"
+"Yet-Another-Name"\t"14\"""")
 
     def _shift_resizes_rectangular_selection(self):
         pyperclip.copy("old clipboard contents")
@@ -160,8 +166,8 @@ class CopySelectedQueryResultsFeatureTest(BaseFeatureTest):
             Keys.CONTROL
         ).send_keys('c').key_up(Keys.CONTROL).perform()
 
-        self.assertEqual("""\"Some-Other-Name"\t"22"\t"some other info"
-"Yet-Another-Name"\t"14"\t"cool info\"""", pyperclip.paste())
+        pyperclip.paste() | should.equal("""\"Some-Other-Name"\t"22"\t"some other info"
+"Yet-Another-Name"\t"14"\t"cool info\"""")
 
     def _shift_resizes_column_selection(self):
         pyperclip.copy("old clipboard contents")
@@ -177,11 +183,10 @@ class CopySelectedQueryResultsFeatureTest(BaseFeatureTest):
         ActionChains(self.page.driver).key_down(
             Keys.CONTROL).send_keys('c').key_up(Keys.CONTROL).perform()
 
-        self.assertEqual(
+        pyperclip.paste() | should.equal(
             """\"Some-Name"\t"6"
 "Some-Other-Name"\t"22"
-"Yet-Another-Name"\t"14\"""",
-            pyperclip.paste())
+"Yet-Another-Name"\t"14\"""")
 
     def _mouseup_outside_grid_still_makes_a_selection(self):
         pyperclip.copy("old clipboard contents")
@@ -200,18 +205,4 @@ class CopySelectedQueryResultsFeatureTest(BaseFeatureTest):
         ActionChains(self.page.driver).key_down(
             Keys.CONTROL).send_keys('c').key_up(Keys.CONTROL).perform()
 
-        self.assertIn('"cool info"', pyperclip.paste())
-
-    def after(self):
-        self.page.close_query_tool()
-        self.page.remove_server(self.server)
-
-        connection = test_utils.get_db_connection(
-            self.server['db'],
-            self.server['username'],
-            self.server['db_password'],
-            self.server['host'],
-            self.server['port'],
-            self.server['sslmode']
-        )
-        test_utils.drop_database(connection, "acceptance_test_db")
+        pyperclip.paste() | should.contain('"cool info"')
diff --git a/web/pgadmin/feature_tests/datatype_test.json b/web/regression/feature_tests/datatype_test.json
similarity index 100%
rename from web/pgadmin/feature_tests/datatype_test.json
rename to web/regression/feature_tests/datatype_test.json
diff --git a/web/pgadmin/feature_tests/keyboard_shortcut_test.py b/web/regression/feature_tests/keyboard_shortcut_test.py
similarity index 82%
rename from web/pgadmin/feature_tests/keyboard_shortcut_test.py
rename to web/regression/feature_tests/keyboard_shortcut_test.py
index bbae1940..0a8d9ff0 100644
--- a/web/pgadmin/feature_tests/keyboard_shortcut_test.py
+++ b/web/regression/feature_tests/keyboard_shortcut_test.py
@@ -8,28 +8,31 @@
 ##########################################################################
 
 from __future__ import print_function
-import time
+
 import sys
+import time
 
-from selenium.webdriver.support.ui import WebDriverWait
-from selenium.webdriver.support import expected_conditions as EC
-from selenium.webdriver.common.by import By
+from grappa import should
 from selenium.webdriver import ActionChains
-from regression.feature_utils.base_feature_test import BaseFeatureTest
+from selenium.webdriver.common.by import By
 from selenium.webdriver.common.keys import Keys
+from selenium.webdriver.support import expected_conditions as EC
+from selenium.webdriver.support.ui import WebDriverWait
+
+from regression.feature_utils.base_feature_test import BaseFeatureTest
 
 
-class KeyboardShortcutFeatureTest(BaseFeatureTest):
-    """
+class TestKeyboardShortcut(BaseFeatureTest):
+    def test_keyboard_shortcut(self, driver):
+        """
         This feature test will test the keyboard short is working
         properly.
-    """
+        """
 
-    scenarios = [
-        ("Test for keyboard shortcut", dict())
-    ]
+        self.driver = driver
+
+        self.setUp()
 
-    def before(self):
         self.new_shortcuts = {
             'mnu_file': {
                 'shortcut': [Keys.ALT, Keys.SHIFT, 'i'],
@@ -43,18 +46,19 @@ class KeyboardShortcutFeatureTest(BaseFeatureTest):
 
         self.wait = WebDriverWait(self.page.driver, 10)
 
-    def runTest(self):
         self._update_preferences()
+
         # On updating keyboard shortcuts, preference cache is updated.
         # There is no UI event through which we can identify that the cache
         # is updated, So, added time.sleep()
         time.sleep(1)
+
         self._check_shortcuts()
 
     def _check_shortcuts(self):
         action = ActionChains(self.driver)
-        for s in self.new_shortcuts:
-            key_combo = self.new_shortcuts[s]['shortcut']
+        for shortcut in self.new_shortcuts:
+            key_combo = self.new_shortcuts[shortcut]['shortcut']
             action.key_down(
                 key_combo[0]
             ).key_down(
@@ -67,20 +71,25 @@ class KeyboardShortcutFeatureTest(BaseFeatureTest):
                 key_combo[1]
             ).perform()
 
-            print("Executing shortcut: " + self.new_shortcuts[s]['locator'] +
-                  "...", file=sys.stderr, end="")
+            print(
+                "Executing shortcut: " +
+                self.new_shortcuts[shortcut]['locator'] +
+                "...",
+                file=sys.stderr, end=""
+            )
 
             self.wait.until(
                 EC.presence_of_element_located(
                     (By.XPATH, "//li[contains(@id, " +
-                     s +
+                     shortcut +
                      ") and contains(@class, 'open')]")
                 )
             )
 
-            is_open = 'open' in self.page.find_by_id(s).get_attribute('class')
-
-            assert is_open is True, "Keyboard shortcut change is unsuccessful."
+            self.page.find_by_id(shortcut).get_attribute('class') | \
+                should.contain(
+                    'open',
+                    msg='Keyboard shortcut change is unsuccessful.')
 
             print("OK", file=sys.stderr)
 
diff --git a/web/pgadmin/feature_tests/pg_datatype_validation_test.py b/web/regression/feature_tests/pg_datatype_validation_test.py
similarity index 85%
rename from web/pgadmin/feature_tests/pg_datatype_validation_test.py
rename to web/regression/feature_tests/pg_datatype_validation_test.py
index 3e34c175..c23d08a6 100644
--- a/web/pgadmin/feature_tests/pg_datatype_validation_test.py
+++ b/web/regression/feature_tests/pg_datatype_validation_test.py
@@ -7,17 +7,19 @@
 #
 ##########################################################################
 
-import os
 import json
+import os
 import time
+
+from grappa import should
 from selenium.common.exceptions import TimeoutException
-from selenium.webdriver.support.ui import WebDriverWait
-from selenium.webdriver.support import expected_conditions as EC
-from selenium.webdriver.common.by import By
 from selenium.webdriver import ActionChains
-from regression.python_test_utils import test_utils
-from regression.feature_utils.base_feature_test import BaseFeatureTest
+from selenium.webdriver.common.by import By
+from selenium.webdriver.support import expected_conditions as EC
+from selenium.webdriver.support.ui import WebDriverWait
 
+from regression.feature_utils.base_feature_test import BaseFeatureTest
+from regression.python_test_utils import test_utils
 
 CURRENT_PATH = os.path.dirname(os.path.realpath(__file__))
 
@@ -31,17 +33,16 @@ except Exception as e:
     print(str(e))
 
 
-class PGDataypeFeatureTest(BaseFeatureTest):
-    """
-        This feature test will test the different Postgres
-        data-type output.
-    """
+class TestPGDataype(BaseFeatureTest):
+    def test_pg_datatype(self, driver):
+        """
+            This feature test will test the different Postgres
+            data-type output.
+        """
+        self.driver = driver
 
-    scenarios = [
-        ("Test checks for PG data-types output", dict())
-    ]
+        self.setUp()
 
-    def before(self):
         connection = test_utils.get_db_connection(
             self.server['db'],
             self.server['username'],
@@ -76,6 +77,25 @@ class PGDataypeFeatureTest(BaseFeatureTest):
         # to add matching closing bracket by it self.
         self._update_preferences()
 
+        self.page.wait_for_spinner_to_disappear()
+        self.page.add_server(self.server)
+        self._schema_node_expandable()
+
+        # Check data types
+        self._check_datatype()
+        self.page.close_query_tool()
+
+        self.page.remove_server(self.server)
+        connection = test_utils.get_db_connection(
+            self.server['db'],
+            self.server['username'],
+            self.server['db_password'],
+            self.server['host'],
+            self.server['port'],
+            self.server['sslmode']
+        )
+        test_utils.drop_database(connection, "acceptance_test_db")
+
     def _update_preferences(self):
         self.page.find_by_id("mnu_file").click()
         self.page.find_by_id("mnu_preferences").click()
@@ -100,7 +120,7 @@ class PGDataypeFeatureTest(BaseFeatureTest):
             "contains(.,'Insert bracket pairs?')]"
         )
 
-        switch_btn = insert_bracket_pairs_control.\
+        switch_btn = insert_bracket_pairs_control. \
             find_element_by_class_name('bootstrap-switch')
 
         # check if switch is on then only toggle.
@@ -126,27 +146,6 @@ class PGDataypeFeatureTest(BaseFeatureTest):
         self.page.find_by_id("btn-flash").click()
         self._clear_query_tool()
 
-    def runTest(self):
-        self.page.wait_for_spinner_to_disappear()
-        self.page.add_server(self.server)
-        self._schema_node_expandable()
-
-        # Check data types
-        self._check_datatype()
-        self.page.close_query_tool()
-
-    def after(self):
-        self.page.remove_server(self.server)
-        connection = test_utils.get_db_connection(
-            self.server['db'],
-            self.server['username'],
-            self.server['db_password'],
-            self.server['host'],
-            self.server['port'],
-            self.server['sslmode']
-        )
-        test_utils.drop_database(connection, "acceptance_test_db")
-
     def _schema_node_expandable(self):
         self.page.toggle_open_tree_item(self.server['name'])
         self.page.toggle_open_tree_item('Databases')
@@ -158,7 +157,7 @@ class PGDataypeFeatureTest(BaseFeatureTest):
         self.page.open_query_tool()
         self._create_enum_type()
         for batch in config_data:
-            query = self.construct_select_query(batch)
+            query = self._construct_select_query(batch)
             self.page.fill_codemirror_area_with(query)
             self.page.find_by_id("btn-flash").click()
             wait = WebDriverWait(self.page.driver, 5)
@@ -179,35 +178,30 @@ class PGDataypeFeatureTest(BaseFeatureTest):
             cells = canvas.find_elements_by_css_selector('.slick-cell')
             # remove first element as it is row number.
             cells.pop(0)
-            for val, cell, datatype in zip(
-                    batch['output'], cells, batch['datatype']):
+            for val, cell, datatype in zip(batch['output'],
+                                           cells,
+                                           batch['datatype']):
                 expected_output = batch['output'][cnt - 2]
 
                 if not self._is_datatype_available_in_current_database(
-                        datatype):
+                   datatype):
                     cnt += 1
                     continue
 
                 if datatype in ('tstzrange', 'tstzrange[]'):
                     expected_output = expected_output.format(
                         **dict([('tz', self.timezone_hh_mm)]))
-                try:
-                    source_code = cell.text
-                    PGDataypeFeatureTest.check_result(
-                        datatype,
-                        source_code,
-                        expected_output
-                    )
 
-                    cnt += 1
-                except TimeoutException:
-                    assert False,\
-                        "for datatype {0}\n{1} does not match with {2}".format(
-                            datatype, val, expected_output
-                        )
+                source_code = cell.text
+                (lambda: TestPGDataype.check_result(
+                    datatype,
+                    source_code,
+                    expected_output
+                )) | should.does_not.raises(TimeoutException)
+
             self._clear_query_tool()
 
-    def construct_select_query(self, batch):
+    def _construct_select_query(self, batch):
         query = 'SELECT '
         first = True
         for datatype, inputdata in zip(batch['datatype'], batch['input']):
@@ -231,17 +225,18 @@ class PGDataypeFeatureTest(BaseFeatureTest):
 
     @staticmethod
     def check_result(datatype, source_code, string_to_find):
-        assert source_code == string_to_find,\
-            "for datatype {0}\n{1} does not match with {2}".format(
-                datatype, source_code, string_to_find
-            )
+        source_code | \
+            should.equal(
+                string_to_find,
+                msg="for datatype {0}\n{1} does not match with {2}".format(
+                    datatype, source_code, string_to_find))
 
     def _clear_query_tool(self):
         self.page.click_element(
             self.page.find_by_xpath("//*[@id='btn-clear-dropdown']")
         )
-        ActionChains(self.driver)\
-            .move_to_element(self.page.find_by_xpath("//*[@id='btn-clear']"))\
+        ActionChains(self.driver) \
+            .move_to_element(self.page.find_by_xpath("//*[@id='btn-clear']")) \
             .perform()
         self.page.click_element(
             self.page.find_by_xpath("//*[@id='btn-clear']")
diff --git a/web/pgadmin/feature_tests/query_tool_journey_test.py b/web/regression/feature_tests/query_tool_journey_test.py
similarity index 83%
rename from web/pgadmin/feature_tests/query_tool_journey_test.py
rename to web/regression/feature_tests/query_tool_journey_test.py
index ec120439..26282979 100644
--- a/web/pgadmin/feature_tests/query_tool_journey_test.py
+++ b/web/regression/feature_tests/query_tool_journey_test.py
@@ -8,24 +8,23 @@
 ##########################################################################
 
 import pyperclip
-
+from grappa import should
 from selenium.webdriver import ActionChains
 from selenium.webdriver.common.keys import Keys
 
-from regression.python_test_utils import test_utils
 from regression.feature_utils.base_feature_test import BaseFeatureTest
+from regression.python_test_utils import test_utils
 
 
-class QueryToolJourneyTest(BaseFeatureTest):
-    """
-    Tests the path through the query tool
-    """
+class TestQueryToolJourney(BaseFeatureTest):
+    def test_table_ddl(self, driver):
+        """
+        Tests the path through the query tool
+        """
+        self.driver = driver
 
-    scenarios = [
-        ("Tests the path through the query tool", dict())
-    ]
+        self.setUp()
 
-    def before(self):
         connection = test_utils.get_db_connection(
             self.server['db'],
             self.server['username'],
@@ -39,7 +38,6 @@ class QueryToolJourneyTest(BaseFeatureTest):
             self.server, "acceptance_test_db", "test_table")
         self.page.add_server(self.server)
 
-    def runTest(self):
         self._navigate_to_query_tool()
 
         self._execute_query(
@@ -50,6 +48,16 @@ class QueryToolJourneyTest(BaseFeatureTest):
         self._test_copies_columns()
         self._test_history_tab()
 
+        self.page.close_query_tool()
+        self.page.remove_server(self.server)
+
+        connection = test_utils.get_db_connection(self.server['db'],
+                                                  self.server['username'],
+                                                  self.server['db_password'],
+                                                  self.server['host'],
+                                                  self.server['port'])
+        test_utils.drop_database(connection, "acceptance_test_db")
+
     def _test_copies_rows(self):
         pyperclip.copy("old clipboard contents")
         self.page.driver.switch_to.default_content()
@@ -59,8 +67,7 @@ class QueryToolJourneyTest(BaseFeatureTest):
             "//*[contains(@class, 'slick-row')]/*[1]").click()
         self.page.find_by_xpath("//*[@id='btn-copy-row']").click()
 
-        self.assertEqual('"Some-Name"\t"6"\t"some info"',
-                         pyperclip.paste())
+        pyperclip.paste() | should.equal('"Some-Name"\t"6"\t"some info"')
 
     def _test_copies_columns(self):
         pyperclip.copy("old clipboard contents")
@@ -74,9 +81,9 @@ class QueryToolJourneyTest(BaseFeatureTest):
         ).click()
         self.page.find_by_xpath("//*[@id='btn-copy-row']").click()
 
-        self.assertTrue('"Some-Name"' in pyperclip.paste())
-        self.assertTrue('"Some-Other-Name"' in pyperclip.paste())
-        self.assertTrue('"Yet-Another-Name"' in pyperclip.paste())
+        pyperclip.paste() | should.contain('"Some-Name"')
+        pyperclip.paste() | should.contain('"Some-Other-Name"')
+        pyperclip.paste() | should.contain('"Yet-Another-Name"')
 
     def _test_history_tab(self):
         self.__clear_query_tool()
@@ -87,30 +94,29 @@ class QueryToolJourneyTest(BaseFeatureTest):
         self.page.click_tab("Query History")
         selected_history_entry = self.page.find_by_css_selector(
             "#query_list .selected")
-        self.assertIn("SELECT * FROM table_that_doesnt_exist",
-                      selected_history_entry.text)
+        selected_history_entry.text | should.contain(
+            "SELECT * FROM table_that_doesnt_exist")
         failed_history_detail_pane = self.page.find_by_id("query_detail")
 
-        self.assertIn(
+        failed_history_detail_pane.text | should.contain(
             "Error Message relation \"table_that_doesnt_exist\" "
-            "does not exist", failed_history_detail_pane.text
-        )
+            "does not exist")
         ActionChains(self.page.driver) \
             .send_keys(Keys.ARROW_DOWN) \
             .perform()
         selected_history_entry = self.page.find_by_css_selector(
             "#query_list .selected")
-        self.assertIn("SELECT * FROM test_table ORDER BY value",
-                      selected_history_entry.text)
+        selected_history_entry.text | should.contain(
+            "SELECT * FROM test_table ORDER BY value")
         selected_history_detail_pane = self.page.find_by_id("query_detail")
-        self.assertIn("SELECT * FROM test_table ORDER BY value",
-                      selected_history_detail_pane.text)
+        selected_history_detail_pane.text | should.contain(
+            "SELECT * FROM test_table ORDER BY value")
         newly_selected_history_entry = self.page.find_by_xpath(
             "//*[@id='query_list']/ul/li[2]")
         self.page.click_element(newly_selected_history_entry)
         selected_history_detail_pane = self.page.find_by_id("query_detail")
-        self.assertIn("SELECT * FROM table_that_doesnt_exist",
-                      selected_history_detail_pane.text)
+        selected_history_detail_pane.text | should.contain(
+            "SELECT * FROM table_that_doesnt_exist")
 
         self.__clear_query_tool()
 
@@ -156,8 +162,8 @@ class QueryToolJourneyTest(BaseFeatureTest):
         self.page.click_element(
             self.page.find_by_xpath("//*[@id='btn-clear-dropdown']")
         )
-        ActionChains(self.driver)\
-            .move_to_element(self.page.find_by_xpath("//*[@id='btn-clear']"))\
+        ActionChains(self.driver) \
+            .move_to_element(self.page.find_by_xpath("//*[@id='btn-clear']")) \
             .perform()
         self.page.click_element(
             self.page.find_by_xpath("//*[@id='btn-clear']")
@@ -176,14 +182,3 @@ class QueryToolJourneyTest(BaseFeatureTest):
 
     def _assert_clickable(self, element):
         self.page.click_element(element)
-
-    def after(self):
-        self.page.close_query_tool()
-        self.page.remove_server(self.server)
-
-        connection = test_utils.get_db_connection(self.server['db'],
-                                                  self.server['username'],
-                                                  self.server['db_password'],
-                                                  self.server['host'],
-                                                  self.server['port'])
-        test_utils.drop_database(connection, "acceptance_test_db")
diff --git a/web/pgadmin/feature_tests/query_tool_tests.py b/web/regression/feature_tests/query_tool_tests.py
similarity index 95%
rename from web/pgadmin/feature_tests/query_tool_tests.py
rename to web/regression/feature_tests/query_tool_tests.py
index ac463d35..5b81f91d 100644
--- a/web/pgadmin/feature_tests/query_tool_tests.py
+++ b/web/regression/feature_tests/query_tool_tests.py
@@ -8,12 +8,12 @@
 ##########################################################################
 
 from __future__ import print_function
-import time
 import sys
 
 from selenium.common.exceptions import StaleElementReferenceException
 
 import config
+from grappa import should
 from selenium.webdriver import ActionChains
 from selenium.webdriver.support.ui import WebDriverWait
 from selenium.webdriver.support import expected_conditions as EC
@@ -22,16 +22,15 @@ from regression.python_test_utils import test_utils
 from regression.feature_utils.base_feature_test import BaseFeatureTest
 
 
-class QueryToolFeatureTest(BaseFeatureTest):
-    """
-        This feature test will test the different query tool features.
-    """
+class TestQueryTool(BaseFeatureTest):
+    def test_query_tool(self, driver):
+        """
+            This feature test will test the different query tool features.
+        """
+        self.driver = driver
 
-    scenarios = [
-        ("Query tool feature test", dict())
-    ]
+        self.setUp()
 
-    def before(self):
         connection = test_utils.get_db_connection(
             self.server['db'],
             self.server['username'],
@@ -48,7 +47,6 @@ class QueryToolFeatureTest(BaseFeatureTest):
         self.page.open_query_tool()
         self._reset_options()
 
-    def runTest(self):
         # on demand result set on scrolling.
         print("\nOn demand query result... ",
               file=sys.stderr, end="")
@@ -104,7 +102,6 @@ class QueryToolFeatureTest(BaseFeatureTest):
         self._query_tool_notify_statements()
         self._clear_query_tool()
 
-    def after(self):
         self.page.remove_server(self.server)
         connection = test_utils.get_db_connection(
             self.server['db'],
@@ -379,10 +376,11 @@ SELECT relname FROM pg_class
             "//div[contains(@class, 'slick-cell') and "
             "contains(text(), '{}')]".format(table_name))
 
-        assert len(el) == 0, "Table '{}' created with auto commit disabled " \
-                             "and without any explicit commit.".format(
-            table_name
-        )
+        len(el) | \
+            should.have.length.of(0,
+                                  msg="Table '{}' created with auto commit "
+                                      "disabled and without any explicit "
+                                      "commit.".format(table_name))
 
     def _query_tool_auto_commit_enabled(self):
 
@@ -465,8 +463,10 @@ SELECT relname FROM pg_class
             "//div[contains(@class, 'slick-cell') and "
             "contains(text(), '{}')]".format(table_name))
 
-        assert len(el) != 0, "Table '{}' is not created with auto " \
-                             "commit enabled.".format(table_name)
+        len(el) | should.not_have.length.of(0,
+                                            msg="Table '{}' is not created "
+                                                "with auto commit "
+                                                "enabled.".format(table_name))
 
     def _query_tool_auto_rollback_enabled(self):
         table_name = 'query_tool_auto_rollback_enabled_table'
@@ -563,8 +563,10 @@ SELECT relname FROM pg_class
             "//div[contains(@class, 'slick-cell') and "
             "contains(text(), '{}')]".format(table_name))
 
-        assert len(el) == 0, "Table '{}' created even after ROLLBACK due to " \
-                             "sql error.".format(table_name)
+        len(el) | should.have.length.of(0,
+                                        msg="Table '{}' created even after "
+                                            "ROLLBACK due to sql "
+                                            "error.".format(table_name))
 
     def _query_tool_cancel_query(self):
         query = """-- 1. END any open transaction.
diff --git a/web/pgadmin/feature_tests/table_ddl_feature_test.py b/web/regression/feature_tests/table_ddl_feature_test.py
similarity index 89%
rename from web/pgadmin/feature_tests/table_ddl_feature_test.py
rename to web/regression/feature_tests/table_ddl_feature_test.py
index 18760019..20d54d7f 100644
--- a/web/pgadmin/feature_tests/table_ddl_feature_test.py
+++ b/web/regression/feature_tests/table_ddl_feature_test.py
@@ -6,19 +6,16 @@
 # This software is released under the PostgreSQL Licence
 #
 ##########################################################################
-
 from regression.feature_utils.base_feature_test import BaseFeatureTest
 from regression.python_test_utils import test_utils
 
 
-class TableDdlFeatureTest(BaseFeatureTest):
-    """ This class test acceptance test scenarios """
+class TestTableDdl(BaseFeatureTest):
+    def test_table_ddl(self, driver):
+        self.driver = driver
 
-    scenarios = [
-        ("Test table DDL generation", dict())
-    ]
+        self.setUp()
 
-    def before(self):
         connection = test_utils.get_db_connection(
             self.server['db'],
             self.server['username'],
@@ -33,7 +30,6 @@ class TableDdlFeatureTest(BaseFeatureTest):
 
         self.page.add_server(self.server)
 
-    def runTest(self):
         test_utils.create_table(
             self.server, "acceptance_test_db", "test_table")
 
@@ -50,7 +46,6 @@ class TableDdlFeatureTest(BaseFeatureTest):
             "//*[contains(@class,'CodeMirror-lines') and "
             "contains(.,'CREATE TABLE public.test_table')]")
 
-    def after(self):
         self.page.remove_server(self.server)
         connection = test_utils.get_db_connection(
             self.server['db'],
diff --git a/web/pgadmin/feature_tests/test_data.json b/web/regression/feature_tests/test_data.json
similarity index 100%
rename from web/pgadmin/feature_tests/test_data.json
rename to web/regression/feature_tests/test_data.json
diff --git a/web/pgadmin/feature_tests/view_data_dml_queries.py b/web/regression/feature_tests/view_data_dml_queries.py
similarity index 92%
rename from web/pgadmin/feature_tests/view_data_dml_queries.py
rename to web/regression/feature_tests/view_data_dml_queries.py
index f5dc6590..0cd2ba1d 100644
--- a/web/pgadmin/feature_tests/view_data_dml_queries.py
+++ b/web/regression/feature_tests/view_data_dml_queries.py
@@ -10,6 +10,8 @@
 import json
 import os
 import time
+
+from grappa import should
 from selenium.webdriver import ActionChains
 from regression.python_test_utils import test_utils
 from regression.feature_utils.base_feature_test import BaseFeatureTest
@@ -27,7 +29,7 @@ except Exception as e:
     print(str(e))
 
 
-class CheckForViewDataTest(BaseFeatureTest):
+class TestCheckForViewData(BaseFeatureTest):
     """
     Test cases to validate insert, update operations in table
     with input test data
@@ -43,12 +45,6 @@ class CheckForViewDataTest(BaseFeatureTest):
         4) Copy/Paste row
     """
 
-    scenarios = [
-        ("Validate Insert, Update operations in View/Edit data with "
-         "given test data",
-         dict())
-    ]
-
     TIMEOUT_STRING = "Timed out waiting for div element to appear"
 
     # query for creating 'defaults_text' table
@@ -80,7 +76,11 @@ CREATE TABLE public.defaults_{0}
 )
 """
 
-    def before(self):
+    def test_check_for_view_data(self, driver):
+        self.driver = driver
+
+        self.setUp()
+
         with test_utils.Database(self.server) as (connection, _):
             if connection.server_version < 90100:
                 self.skipTest(
@@ -103,12 +103,11 @@ CREATE TABLE public.defaults_{0}
             test_utils.create_table_with_query(
                 self.server,
                 "acceptance_test_db",
-                CheckForViewDataTest.defaults_query.format(k, v))
+                TestCheckForViewData.defaults_query.format(k, v))
 
         # Initialize an instance of WebDriverWait with timeout of 3 seconds
         self.wait = WebDriverWait(self.driver, 3)
 
-    def runTest(self):
         self.page.wait_for_spinner_to_disappear()
         self.page.add_server(self.server)
         self._tables_node_expandable()
@@ -127,7 +126,6 @@ CREATE TABLE public.defaults_{0}
             self._copy_paste_row()
             self.page.close_data_grid()
 
-    def after(self):
         self.page.remove_server(self.server)
         connection = test_utils.get_db_connection(
             self.server['db'],
@@ -161,12 +159,12 @@ CREATE TABLE public.defaults_{0}
         try:
             wait.until(EC.text_to_be_present_in_element(
                 (By.XPATH, xpath + "//span"), str(value)),
-                CheckForViewDataTest.TIMEOUT_STRING
+                TestCheckForViewData.TIMEOUT_STRING
             )
         except Exception:
             wait.until(EC.text_to_be_present_in_element(
                 (By.XPATH, xpath), str(value)),
-                CheckForViewDataTest.TIMEOUT_STRING
+                TestCheckForViewData.TIMEOUT_STRING
             )
 
     def _update_cell(self, xpath, data):
@@ -182,7 +180,7 @@ CREATE TABLE public.defaults_{0}
         """
 
         self.wait.until(EC.visibility_of_element_located(
-            (By.XPATH, xpath)), CheckForViewDataTest.TIMEOUT_STRING
+            (By.XPATH, xpath)), TestCheckForViewData.TIMEOUT_STRING
         )
         cell_el = self.page.find_by_xpath(xpath)
         self.page.driver.execute_script("arguments[0].scrollIntoView()",
@@ -247,16 +245,16 @@ CREATE TABLE public.defaults_{0}
         self.wait.until(
             EC.visibility_of_element_located(
                 (By.CSS_SELECTOR, 'iframe')
-            ), CheckForViewDataTest.TIMEOUT_STRING
+            ), TestCheckForViewData.TIMEOUT_STRING
         )
         self.page.driver.switch_to.frame(
             self.page.driver.find_element_by_tag_name('iframe')
         )
 
     def _copy_paste_row(self):
-        row0_cell0_xpath = CheckForViewDataTest._get_cell_xpath("r0", 1)
-        row1_cell1_xpath = CheckForViewDataTest._get_cell_xpath("r1", 2)
-        row1_cell2_xpath = CheckForViewDataTest._get_cell_xpath("r2", 2)
+        row0_cell0_xpath = TestCheckForViewData._get_cell_xpath("r0", 1)
+        row1_cell1_xpath = TestCheckForViewData._get_cell_xpath("r1", 2)
+        row1_cell2_xpath = TestCheckForViewData._get_cell_xpath("r2", 2)
 
         self.page.find_by_xpath(row0_cell0_xpath).click()
         self.page.find_by_xpath("//*[@id='btn-copy-row']").click()
@@ -264,7 +262,7 @@ CREATE TABLE public.defaults_{0}
         # Update primary key of copied cell
         self._update_cell(row1_cell1_xpath, [2, "", "int"])
         self.page.find_by_xpath(
-            CheckForViewDataTest._get_cell_xpath("r1", "3")
+            TestCheckForViewData._get_cell_xpath("r1", "3")
         ).click()
 
         # Check if removing a cell value with default value sets
@@ -272,7 +270,7 @@ CREATE TABLE public.defaults_{0}
         self._update_cell(row1_cell2_xpath, ["clear", "", "int"])
         # click outside
         self.page.find_by_xpath(
-            CheckForViewDataTest._get_cell_xpath("r1", "3")
+            TestCheckForViewData._get_cell_xpath("r1", "3")
         ).click()
 
         self._compare_cell_value(row1_cell2_xpath, "[default]")
@@ -291,7 +289,7 @@ CREATE TABLE public.defaults_{0}
 
     def _add_row(self):
         for idx in range(1, len(config_data.keys()) + 1):
-            cell_xpath = CheckForViewDataTest._get_cell_xpath(
+            cell_xpath = TestCheckForViewData._get_cell_xpath(
                 'r' + str(idx), 1
             )
             time.sleep(0.2)
@@ -326,7 +324,7 @@ CREATE TABLE public.defaults_{0}
                                             element)
 
             if (idx != 1 and not is_new_row) or is_new_row:
-                self.assertEquals(element.text, config_data[str(idx)][1])
+                element.text | should.equal(config_data[str(idx)][1])
 
         # scroll browser back to the left
         # to reset position so other assertions can succeed
diff --git a/web/pgadmin/feature_tests/xss_checks_file_manager_test.py b/web/regression/feature_tests/xss_checks_file_manager_test.py
similarity index 84%
rename from web/pgadmin/feature_tests/xss_checks_file_manager_test.py
rename to web/regression/feature_tests/xss_checks_file_manager_test.py
index 60d7e91c..ae5aa282 100644
--- a/web/pgadmin/feature_tests/xss_checks_file_manager_test.py
+++ b/web/regression/feature_tests/xss_checks_file_manager_test.py
@@ -8,24 +8,23 @@
 ##########################################################################
 
 import os
-import time
-from selenium.webdriver.common.keys import Keys
-from selenium.webdriver.support.ui import WebDriverWait
+
+from grappa import should
 from selenium.webdriver.common.by import By
+from selenium.webdriver.common.keys import Keys
 from selenium.webdriver.support import expected_conditions as EC
-from regression.python_test_utils import test_utils
+from selenium.webdriver.support.ui import WebDriverWait
+
 from regression.feature_utils.base_feature_test import BaseFeatureTest
+from regression.python_test_utils import test_utils
 
 
-class CheckFileManagerFeatureTest(BaseFeatureTest):
-    """Tests to check file manager for XSS."""
+class TestCheckFileManager(BaseFeatureTest):
+    def test_check_file_manager(self, driver):
+        self.driver = driver
 
-    scenarios = [
-        ("Tests to check if File manager is vulnerable to XSS",
-         dict())
-    ]
+        self.setUp()
 
-    def before(self):
         connection = test_utils.get_db_connection(
             self.server['db'],
             self.server['username'],
@@ -42,7 +41,11 @@ class CheckFileManagerFeatureTest(BaseFeatureTest):
         if os.path.isfile(self.XSS_FILE):
             os.remove(self.XSS_FILE)
 
-    def after(self):
+        self._navigate_to_query_tool()
+        self.page.fill_codemirror_area_with("SELECT 1;")
+        self._create_new_file()
+        self._open_file_manager_and_check_xss_file()
+
         self.page.close_query_tool('sql', False)
         self.page.remove_server(self.server)
         connection = test_utils.get_db_connection(
@@ -54,12 +57,6 @@ class CheckFileManagerFeatureTest(BaseFeatureTest):
         )
         test_utils.drop_database(connection, "acceptance_test_db")
 
-    def runTest(self):
-        self._navigate_to_query_tool()
-        self.page.fill_codemirror_area_with("SELECT 1;")
-        self._create_new_file()
-        self._open_file_manager_and_check_xss_file()
-
     def _navigate_to_query_tool(self):
         self.page.toggle_open_tree_item(self.server['name'])
         self.page.toggle_open_tree_item('Databases')
@@ -113,14 +110,9 @@ class CheckFileManagerFeatureTest(BaseFeatureTest):
 
         self.page.click_modal('Cancel')
         self.page.wait_for_query_tool_loading_indicator_to_disappear()
-        self._check_escaped_characters(
-            contents,
-            '&lt;img src=x onmouseover=alert("1")&gt;.sql',
-            'File manager'
-        )
 
-    def _check_escaped_characters(self, source_code, string_to_find, source):
-        # For XSS we need to search against element's html code
-        assert source_code.find(
-            string_to_find
-        ) != -1, "{0} might be vulnerable to XSS ".format(source)
+        escaped_characters = '&lt;img src=x onmouseover=alert("1")&gt;.sql'
+        contents | should.contain(
+            escaped_characters,
+            msg="File Manager might be vulnerable to XSS "
+        )
diff --git a/web/pgadmin/feature_tests/xss_checks_panels_and_query_tool_test.py b/web/regression/feature_tests/xss_checks_panels_and_query_tool_test.py
similarity index 87%
rename from web/pgadmin/feature_tests/xss_checks_panels_and_query_tool_test.py
rename to web/regression/feature_tests/xss_checks_panels_and_query_tool_test.py
index 4dc082c2..ea507935 100644
--- a/web/pgadmin/feature_tests/xss_checks_panels_and_query_tool_test.py
+++ b/web/regression/feature_tests/xss_checks_panels_and_query_tool_test.py
@@ -6,31 +6,30 @@
 # This software is released under the PostgreSQL Licence
 #
 ##########################################################################
-
+from grappa import should
 from regression.python_test_utils import test_utils
 from regression.feature_utils.base_feature_test import BaseFeatureTest
 
 
-class CheckForXssFeatureTest(BaseFeatureTest):
-    """
-    Tests to check if pgAdmin4 is vulnerable to XSS.
+class TestCheckForXss(BaseFeatureTest):
+    def test_check_for_xss(self, driver):
+        """
+        Tests to check if pgAdmin4 is vulnerable to XSS.
 
-    Here we will check html source code for escaped characters if we
-    found them in the code then we are not vulnerable otherwise we might.
+        Here we will check html source code for escaped characters if we
+        found them in the code then we are not vulnerable otherwise we might.
 
-    We will cover,
-        1) Browser Tree (aciTree)
-        2) Properties Tab (BackFrom)
-        3) Dependents Tab (BackGrid)
-        4) SQL Tab (Code Mirror)
-        5) Query Tool (SlickGrid)
-    """
+        We will cover,
+            1) Browser Tree (aciTree)
+            2) Properties Tab (BackFrom)
+            3) Dependents Tab (BackGrid)
+            4) SQL Tab (Code Mirror)
+            5) Query Tool (SlickGrid)
+        """
+        self.driver = driver
 
-    scenarios = [
-        ("Test XSS check for panels and query tool", dict())
-    ]
+        self.setUp()
 
-    def before(self):
         connection = test_utils.get_db_connection(
             self.server['db'],
             self.server['username'],
@@ -52,7 +51,6 @@ class CheckForXssFeatureTest(BaseFeatureTest):
             "unique", "<h1 onmouseover='console.log(2);'>Y"
         )
 
-    def runTest(self):
         self.page.wait_for_spinner_to_disappear()
         self.page.add_server(self.server)
         self._tables_node_expandable()
@@ -65,7 +63,6 @@ class CheckForXssFeatureTest(BaseFeatureTest):
         self._check_xss_in_query_tool()
         self.page.close_query_tool()
 
-    def after(self):
         self.page.remove_server(self.server)
         connection = test_utils.get_db_connection(
             self.server['db'],
@@ -165,5 +162,6 @@ class CheckForXssFeatureTest(BaseFeatureTest):
 
     def _check_escaped_characters(self, source_code, string_to_find, source):
         # For XSS we need to search against element's html code
-        assert source_code.find(string_to_find) != - \
-            1, "{0} might be vulnerable to XSS ".format(source)
+        source_code | \
+            should.contain(string_to_find,
+                           msg="{0} might be vulnerable to XSS".format(source))
diff --git a/web/pgadmin/feature_tests/xss_checks_pgadmin_debugger_test.py b/web/regression/feature_tests/xss_checks_pgadmin_debugger_test.py
similarity index 85%
rename from web/pgadmin/feature_tests/xss_checks_pgadmin_debugger_test.py
rename to web/regression/feature_tests/xss_checks_pgadmin_debugger_test.py
index 17ce6153..464bbf1e 100644
--- a/web/pgadmin/feature_tests/xss_checks_pgadmin_debugger_test.py
+++ b/web/regression/feature_tests/xss_checks_pgadmin_debugger_test.py
@@ -6,27 +6,30 @@
 # This software is released under the PostgreSQL Licence
 #
 ##########################################################################
-
-from selenium.webdriver import ActionChains
+import pytest
+from grappa import should
 from selenium.common.exceptions import TimeoutException
-from regression.python_test_utils import test_utils
-from regression.feature_utils.base_feature_test import BaseFeatureTest
-from selenium.webdriver.support.ui import WebDriverWait
-from selenium.webdriver.support import expected_conditions as EC
+from selenium.webdriver import ActionChains
 from selenium.webdriver.common.by import By
+from selenium.webdriver.support import expected_conditions as EC
+from selenium.webdriver.support.ui import WebDriverWait
+
+from regression.feature_utils.base_feature_test import BaseFeatureTest
+from regression.python_test_utils import test_utils
 
 
-class CheckDebuggerForXssFeatureTest(BaseFeatureTest):
-    """Tests to check if Debugger is vulnerable to XSS."""
+class TestCheckDebuggerForXss(BaseFeatureTest):
+    def test_check_debugger_for_xss(self, driver):
+        """
+        Tests to check if Debugger is vulnerable to XSS.
+        """
+        self.driver = driver
 
-    scenarios = [
-        ("Tests to check if Debugger is vulnerable to XSS", dict())
-    ]
+        self.setUp()
 
-    def before(self):
         with test_utils.Database(self.server) as (connection, _):
             if connection.server_version < 90100:
-                self.skipTest(
+                pytest.skip(
                     "Functions tree node is not present in pgAdmin below "
                     "PG v9.1"
                 )
@@ -36,13 +39,11 @@ class CheckDebuggerForXssFeatureTest(BaseFeatureTest):
             self.server, "postgres", "a_test_function"
         )
 
-    def runTest(self):
         self.page.wait_for_spinner_to_disappear()
         self.page.add_server(self.server)
         self._function_node_expandable()
         self._debug_function()
 
-    def after(self):
         self.page.remove_server(self.server)
         test_utils.drop_debug_function(self.server, "postgres",
                                        "a_test_function")
@@ -80,7 +81,7 @@ class CheckDebuggerForXssFeatureTest(BaseFeatureTest):
         # If debugger plugin is not found
         if is_error and is_error.text == "Debugger Error":
             self.page.click_modal('OK')
-            self.skipTest(
+            pytest.skip(
                 "Please make sure that debugger plugin is properly configured"
             )
         else:
@@ -106,11 +107,14 @@ class CheckDebuggerForXssFeatureTest(BaseFeatureTest):
             source_code = self.page.find_by_xpath(
                 "//*[@id='messages']"
             ).get_attribute('innerHTML')
-            self._check_escaped_characters(
-                source_code,
-                'NOTICE:  &lt;img src="x" onerror="console.log(1)"&gt;',
-                'Debugger'
+
+            escaped_characters = \
+                'NOTICE:  &lt;img src="x" onerror="console.log(1)"&gt;'
+            source_code | should.contain(
+                escaped_characters,
+                msg="Debugger might be vulnerable to XSS "
             )
+
             self._close_debugger()
 
     def _close_debugger(self):
@@ -119,8 +123,3 @@ class CheckDebuggerForXssFeatureTest(BaseFeatureTest):
             self.page.find_by_xpath(
                 "//*[@id='dockerContainer']/div/div[3]/div/div[2]/div[1]")
         )
-
-    def _check_escaped_characters(self, source_code, string_to_find, source):
-        # For XSS we need to search against element's html code
-        assert source_code.find(string_to_find) != - \
-            1, "{0} might be vulnerable to XSS ".format(source)
diff --git a/web/pgadmin/feature_tests/xss_checks_roles_control_test.py b/web/regression/feature_tests/xss_checks_roles_control_test.py
similarity index 73%
rename from web/pgadmin/feature_tests/xss_checks_roles_control_test.py
rename to web/regression/feature_tests/xss_checks_roles_control_test.py
index 21a20273..0ce64b3b 100644
--- a/web/pgadmin/feature_tests/xss_checks_roles_control_test.py
+++ b/web/regression/feature_tests/xss_checks_roles_control_test.py
@@ -6,21 +6,18 @@
 # This software is released under the PostgreSQL Licence
 #
 ##########################################################################
+from grappa import should
 
-from selenium.webdriver import ActionChains
-from regression.python_test_utils import test_utils
 from regression.feature_utils.base_feature_test import BaseFeatureTest
+from regression.python_test_utils import test_utils
 
 
-class CheckRoleMembershipControlFeatureTest(BaseFeatureTest):
-    """Tests to check role membership control for xss."""
+class TestCheckRoleMembershipControl(BaseFeatureTest):
+    def test_check_role_membership_control(self, driver):
+        self.driver = driver
 
-    scenarios = [
-        ("Tests to check if Role membership control is vulnerable to XSS",
-         dict())
-    ]
+        self.setUp()
 
-    def before(self):
         with test_utils.Database(self.server) as (connection, _):
             if connection.server_version < 90100:
                 self.skipTest(
@@ -32,13 +29,11 @@ class CheckRoleMembershipControlFeatureTest(BaseFeatureTest):
         test_utils.create_role(self.server, "postgres",
                                "<h1>test</h1>")
 
-    def runTest(self):
         self.page.wait_for_spinner_to_disappear()
         self.page.add_server(self.server)
         self._role_node_expandable()
         self._check_role_membership_control()
 
-    def after(self):
         self.page.remove_server(self.server)
         test_utils.drop_role(self.server, "postgres",
                              "test_role")
@@ -59,17 +54,13 @@ class CheckRoleMembershipControlFeatureTest(BaseFeatureTest):
             "//div[contains(@class,'rolmembership')]"
         ).get_attribute('innerHTML')
 
-        self._check_escaped_characters(
-            source_code,
-            '&lt;h1&gt;test&lt;/h1&gt;',
-            'Role Membership Control'
+        escaped_characters = '&lt;h1&gt;test&lt;/h1&gt;'
+        source_code | should.contain(
+            escaped_characters,
+            msg="Role Membership Control might be vulnerable to XSS "
         )
+
         self.page.find_by_xpath(
             "//button[contains(@type, 'cancel') and "
             "contains(.,'Cancel')]"
         ).click()
-
-    def _check_escaped_characters(self, source_code, string_to_find, source):
-        # For XSS we need to search against element's html code
-        assert source_code.find(string_to_find) != - \
-            1, "{0} might be vulnerable to XSS ".format(source)
diff --git a/web/regression/feature_utils/base_feature_test.py b/web/regression/feature_utils/base_feature_test.py
index 3fcb94bf..f80af969 100644
--- a/web/regression/feature_utils/base_feature_test.py
+++ b/web/regression/feature_utils/base_feature_test.py
@@ -15,14 +15,35 @@ from datetime import datetime
 
 from copy import deepcopy
 
+import pytest
+
 import config as app_config
 from pgadmin.utils.base_test_generator import BaseTestGenerator
 from regression.feature_utils.pgadmin_page import PgadminPage
+from regression.feature_utils.app_starter import AppStarter
+from regression.python_test_utils import test_utils
+
+import atexit
 
 
 class BaseFeatureTest(BaseTestGenerator):
     CURRENT_PATH = os.path.dirname(os.path.realpath(__file__))
 
+    @pytest.fixture(autouse=True)
+    def something(self, request):
+        yield
+        # request.node is an "item" because we use the default
+        # "function" scope
+        if request.node.rep_setup.failed:
+            self._screenshot()
+        elif request.node.rep_setup.passed:
+            if request.node.rep_call.failed:
+                self._screenshot()
+
+    def startApp(self):
+        self.app_starter = AppStarter(self.driver, app_config)
+        self.app_starter.start_app()
+
     def setUp(self):
         self.server = deepcopy(self.server)
         self.server['name'] += ' Feature Tests'
@@ -45,9 +66,6 @@ class BaseFeatureTest(BaseTestGenerator):
             self._screenshot()
             raise
 
-    def runTest(self):
-        pass
-
     def before(self):
         pass
 
-- 
2.17.0

