Changeset - b4b79380b3d0
[Not reviewed]
tip default
0 1 2
Branko Majic (branko) - 6 years ago 2018-01-04 21:04:56
branko@majic.rs
CONNT-34: Implemented base functional test class:

- Added helper class to be used as common ancestor for all functional
tests.
- Added missing headers.
- Updated hgignore file to exclude the geckodriver.log.
3 files changed with 155 insertions and 1 deletions:
0 comments (0 inline, 0 general)
.hgignore
Show inline comments
 
@@ -8,4 +8,5 @@ tmp/
 
dist/
 
django_conntrackt.egg-info/
 
projtect/.coverage
 
test_binaries/
 
\ No newline at end of file
 
test_binaries/
 
geckodriver.log
 
\ No newline at end of file
testproject/functional_tests/__init__.py
Show inline comments
 
new file 100644
 
# -*- coding: utf-8 -*-
 
#
 
# Copyright (C) 2017 Branko Majic
 
#
 
# This file is part of Django Conntrackt.
 
#
 
# Django Conntrackt is free software: you can redistribute it and/or modify it
 
# under the terms of the GNU General Public License as published by the Free
 
# Software Foundation, either version 3 of the License, or (at your option) any
 
# later version.
 
#
 
# Django Conntrackt is distributed in the hope that it will be useful, but
 
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
 
# details.
 
#
 
# You should have received a copy of the GNU General Public License along with
 
# Django Conntrackt.  If not, see <http://www.gnu.org/licenses/>.
 
#
testproject/functional_tests/base.py
Show inline comments
 
new file 100644
 
# -*- coding: utf-8 -*-
 
#
 
# Copyright (C) 2017 Branko Majic
 
#
 
# This file is part of Django Conntrackt.
 
#
 
# Django Conntrackt is free software: you can redistribute it and/or modify it
 
# under the terms of the GNU General Public License as published by the Free
 
# Software Foundation, either version 3 of the License, or (at your option) any
 
# later version.
 
#
 
# Django Conntrackt is distributed in the hope that it will be useful, but
 
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
 
# details.
 
#
 
# You should have received a copy of the GNU General Public License along with
 
# Django Conntrackt.  If not, see <http://www.gnu.org/licenses/>.
 
#
 

	
 

	
 
# Standard library imports.
 
import os
 
import time
 

	
 
# Django imports.
 
from django.contrib.staticfiles.testing import StaticLiveServerTestCase
 

	
 
# Third-party library imports.
 
from selenium.common.exceptions import WebDriverException
 
from selenium import webdriver
 

	
 

	
 
# Maximum amount of time to wait before assuming a test has
 
# failed. Used as default for functions that wait.
 
MAX_WAIT = 10
 

	
 
# Delay between subsequent runs of function when using the wait
 
# decorator.
 
EXECUTION_DELAY = 0.5
 

	
 
# Base directory where the tests are located at.
 
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
 

	
 
# Base directory where the test binaries are located at.
 
TEST_BINARIES_DIR = os.path.join(BASE_DIR, "..", "..", "test_binaries")
 

	
 
# Location of Firefox binary.
 
FIREFOX_BINARY = os.path.join(TEST_BINARIES_DIR, "firefox", "firefox")
 

	
 
# Location of geckodriver binary.
 
GECKODRIVER_BINARY = os.path.join(TEST_BINARIES_DIR, "geckodriver")
 

	
 

	
 
def wait(fn, execution_delay=EXECUTION_DELAY, max_wait=MAX_WAIT):
 
    """
 
    Decorator that will produce a wrapper function that will run the
 
    specified function repeatedly as long as all of the following
 
    conditions have been met:
 

	
 
    - Function throws either AssertionError or
 
      selenium.common.exceptions.WebDriverException.
 
    - Combined runtime of all function calls does not exceed the
 
      maximum wait time.
 

	
 
    As soon as the original function returns without any thrown
 
    exceptions, the wrapper function will return its result.
 

	
 
    If the function throws any other exception beyond the ones listed
 
    above, this exception will get propagated.
 

	
 
    Arguments:
 

	
 
      fn
 
        Function that should be run.
 

	
 
      max_wait
 
        Maximum time in seconds to wait for function to execute
 
        successfully.
 

	
 
      execution_delay
 
        Delay in seconds between subsequent function runs. Used to
 
        prevent high CPU usage.
 

	
 
    Returns:
 

	
 
      Wrapper function around passed-in function. The wrapper
 
      function, provided it executes without failure, returns the
 
      result of original function.
 
    """
 

	
 
    def modified_fn(*args, **kwargs):
 
        start_time = time.time()
 

	
 
        while True:
 
            try:
 
                return fn(*args, **kwargs)
 
            except (AssertionError, WebDriverException) as e:
 
                if time.time() - start_time > max_wait:
 
                    raise e
 
                time.sleep(execution_delay)
 

	
 
    return modified_fn
 

	
 

	
 
class FunctionalTest(StaticLiveServerTestCase):
 
    """
 
    Helper test class that provides some convenience when writing
 
    functional tests.
 

	
 
    During test set-up, the class will:
 

	
 
    - Set-up a browser instance, storing it in property self.browser.
 

	
 
    During the test tear-down, the class will:
 

	
 
    - Destroy the browser instance.
 

	
 
    In addition to this, the class provides a convenience method
 
    wait_for which can be used to wait for a function to execute
 
    within a time slot and assert something, as described for the wait
 
    decorator.
 
    """
 

	
 
    def setUp(self):
 
        self.browser = webdriver.Firefox(firefox_binary=FIREFOX_BINARY, executable_path=GECKODRIVER_BINARY)
 

	
 
    def tearDown(self):
 
        self.browser.quit()
 
        super(FunctionalTest, self).tearDown()
 

	
 
    @wait
 
    def wait_for(self, fn):
 
        return fn()
0 comments (0 inline, 0 general)