Showing posts with label Webdriver. Show all posts
Showing posts with label Webdriver. Show all posts

Friday, July 25, 2014

Speeding up your test setup by launching your WebDriver asynchronously

There seems to be a much higher overhead to starting new instances of Browsers/WebDriver than it does to perform simple Rest and Database queries typically used in test setup.

Implementing a async wrapper method for instantiating new webdriver

C# offers the keyword 'async' and 'await'.  these 2 keywords allow you to turn methods into methods that return task wrappers that act like promises.

Here's how I put mine async webdriver init together

     public async Task<IWebDriver> NewWebDriverAsync()  
     {  
       var task = Task.Run(() => NewDriver());  
       return await task;  
     }  


Before (5 seconds on IE10):
     [SetUp]  
     public void SetUp()  
     {  
       //This will perform the register instance along with the first upgrade transaction data.  
       var regBuilder =  
         new RestfulRegistrationBuilder().WithApplication(ApplicationEnum.ConciergeService);  
       _registrationData = regBuilder.Build();  
       var upgradeBuilder =  
         new RestfulUpgradeTransactionBuilder().WithHandleAndAppIdFromRegistrationData(_registrationData);  
       _upgradeTransactionData = upgradeBuilder.Build();  
       _driver = NewWebDriver();      
     }  


After (2 seconds on IE10):
     [SetUp]  
     public void SetUp()  
     {  
       var driverInitTask = NewWebDriverAsync();  
       //This will perform the register instance along with the first upgrade transaction data.  
       var regBuilder =  
         new RestfulRegistrationBuilder().WithApplication(ApplicationEnum.ConciergeService);  
       _registrationData = regBuilder.Build();  
       Console.WriteLine("Registered Instance " + _registrationData);  
       var upgradeBuilder =  
         new RestfulUpgradeTransactionBuilder().WithHandleAndAppIdFromRegistrationData(_registrationData);  
       _upgradeTransactionData = upgradeBuilder.Build();  
       Console.WriteLine("Performed Upgrade " + _upgradeTransactionData);  
       _driver = driverInitTask.Result;        
     }  

Thursday, February 7, 2013

WTF is now open source!

I'm happy to announce that Web Test Framework, WTF, is now open source.  You can find the source and installation instructions here:

https://github.com/wiredrive/wtframework

I've taken a lot of product agnostic framework code I've been working on the last couple months and separated it out from  our test scripts into a reusable framework.  I hope that by doing this, we could take web testing into a structured approach with easy to use generators and integrated tools like how various MVC frameworks like Rails and Django have revolutionized web development.

Please check it out and let me know what you think.  I'll be working on trying to get this framework documented and making it more user friendly.


Monday, December 31, 2012

Python : Quick and Dirty PageObject Pattern

One of the basic of good test automation design is to keep low level actions separate from high level business rules.  One way of doing this is keeping a consistent theme of separating screens, windows, and dialogs into manageable self contained objects that hide all the low level UI interactions and only expose higher level transactional actions.

While Java has an official PageFactory implementation.  Those of us using Python are left trying to figure out what works best out of the the many implementations out there.   Here are a few implementations I find notable.
However,  I wanted something slightly different.  1st example I thought was a bit annoying to have to create wrappers for all the page elements, while the 2nd one was trying to implement a test framework (something which I already have).  The 3rd I felt was not a very good way of implementing Page Objects due to having hard coded locators in the method bodies and not enforcing page validation.

So I started thinking of how I'd implement one that was very simple, light weight and easy to understand.  I want to make one that was easy to maintain, debug, and have a very elegant syntax during usage.

Creating a quick and dirty Page Object Base Class

At the very essence, I need the Page Object to do the following:
  1. Validate we are on the correct page.
  2. Keep track of it's elements in 1 centralized place in the Page Object class (all in the top section)
  3. Hide UI level logic and only expose transactional level interactions.

Handling Page Self Validation

To handle the aspect of making pages self validating, we can accomplish this using a simple base class with an abstract method for validating the page.  Then in our PageObject constructor we can make a call to validate the page when the constructor/initializer is called.


import abc

class PageObject(object):

    # Webdriver associated with this instance of the PageObject
    webdriver = None

    def __init__(self, webdriver):
        self._validate_page(webdriver)
        
        self.webdriver = webdriver

    @abc.abstractmethod
    def _validate_page(self, webdriver):
        """
        Perform checks to validate this page is the correct target page.
        
        @raise IncorrectPageException: Raised when we try to assign the wrong page 
        to this page object.
        """
        return

class InvalidPageError(Exception):
    '''Thrown when we have tried to instantiate the incorrect page to a PageObject.'''

Now classes that inherit from PageObject will enforce PageValidation.  In this base PageObject class, we also assign the WebDriver instance, which will be useful for the next step below.

Handling Page Element Mapping


To keep all the Page Element Mappings in one place, I want to accomplish this by making all page element mappings a Class property that's evaluated during run time.  There's 2 ways we can effectively do this in Python, 1) Using class decorators to simplify writing getter methods, or 2) Using Python's lambda expressions to create easy 1 line anonymous functions that are called at run time.  I felt the 2nd was a lot less work to do, so I opted for the latter.  As you can see below, using the lambda functions I'm able to effectively keep all my locators in 1 place in my PageObject class, where they're all in one place and easy to maintain.


class ProjectHomePage(PageObject):
    '''
    Page Object for Project Home Page (page when you view an individual project)
    '''

    # Identifying Properties #
    _BODY_TAG_ID = "project_home"

    ### Web Element Identifiers ###
    to = lambda self:self.webdriver.find_element_by_css_selector("#send-presentation-overlay .to-textarea")
    subject = lambda self:self.webdriver.find_element_by_xpath("//*[@id='send-presentation-overlay']//input[@name='subject']")
    message_text = lambda self:self.webdriver.find_element_by_css_selector("#send-presentation-overlay .message-textarea")
    send_button = lambda self:self.webdriver.find_element_by_css_selector("#send-presentation-overlay .submit")
...
    def send_message(self, to_address, subject, message):

        self.to().send_keys(to_address)
        self.subject().send_keys(subject)
        
        #switch message input to text input to make it easier to set the message.
        self.message_text().send_keys(message)

With the mapping handled by lambda expression, how you can use this lambda expression to referencing the mappings by calling it as a member method. These lambda expressions are handled during runtime and will return the webelement requested using the stored instance of WebDriver from the base PageObject.

Another added benefit of using lambda expressions is it works very well with WebDriverWait statements.  This is very useful for Ajax pages where you are frequently waiting for elements be become visible/enabled. (Note: this is a bit hackish in how I pass in 'self' instead of the webdriver in the webdriver wait. But the end result is the same, the lambda function has a direct reference to the webdriver through the '.webdriver' property.)


WebDriverWait(self, 10).until(self.send_button).click()

How it comes together to hide low level details

Once you have a bunch of these low level transactional details wrapped into PageObject calls.  Writing your high level tests should be a simple matter of just calling your PageObject's methods.  This will automatically do the page validation and perform the actions.


class TestProjectPageTests(unittest.TestCase):
    
    def test_send_message_to_project_manager(self):
        driver = firefox.webdriver.WebDriver()
        LoginPage.go_to_page(driver)
        LoginPage(driver).login("user","password")
        HomePage(driver).go_to_project_page()
        ProjectsListPage(driver).open_project("Project1")
        ProjectPage(driver).send_message_to_project_manager("test message")
        ...

There you have it.  A simple PageObject implementation that not that hard to implement, and does wonders for cleaning up your high level test syntax.



Update:

Since I've written this article a while back.  I've put a lot of this implementation into Web Test Framework (WTF), which my company has open sourced.  You can find WTF, here:  https://github.com/wiredrive/wtframework

Friday, December 28, 2012

Python Selenium - Capturing ScreenShot on Error

As part of the test framework, at the point of error, we'd like to capture as much information as possible, including taking screenshots. In order to do this using a Python Unittest / Selenium framework we did 2 things.  1) Create a screenshot utility function that can work both across a local or remote webdriver. 2) Create a base test where we modified the run method to take a screenshot prior to saving the error message, and

Creating our screen capture utility function

Selenium webdriver offers a couple methods for capturing screenshot.  One works well for local instances of webdriver, and the other while much slower is better to use for RemoteWebDriver as binary data can be unpredictable across scripting over the wire.


class ScreenShotUtil:
    "Screenshot Utility Class"

    @staticmethod
    def take_screenshot(webdriver, file_name="error.png"):
        """
        @param webdriver: WebDriver.
        @type webdriver: WebDriver
        @param file_name: Name to label this screenshot.
        @type file_name: str 
        """
        if isinstance(webdriver, remote.webdriver.WebDriver):
            # Get Screenshot over the wire as base64
            base64_data = webdriver.get_screenshot_as_base64()
            screenshot_data = base64.decodestring(base64_data)
            screenshot_file = open(filename, "w")
            screenshot_file.write(screenshot_data)
            screenshot_file.close()
        else:
            webdriver.save_screenshot(filename)

Adding our screenshot capture rule

In Java's JUnit, you can easily just create a MethodRule (or TestWatcher) which you can use to annotate your tests.  Because Python's UnitTest does not have such a feature to register call backs or event listeners to failed test results, short of implementing or using a 3rd party Test Runner, an easy way to do this is to just create a BaseTest where we override the 'run' method and insert these calls.  To do this,

  1. Open the source code for unittest.TestCase.  You can find it here, http://sourceforge.net/projects/pyunit/
  2. Create a BaseTest class that extends TestCase.
  3. Override the run() method definition with the copy from the latest version.
  4. Inside the exception hander for failed step, and error step, insert your call to your screen capture utility.



...
class ScreenCaptureTestCase(unittest.TestCase):
...
    # Defining an init method so we can pass it a webdriver.
    def __init__(self, methodName='runTest', webdriver=None, screenshot_util=None):
        super(WDBaseTest, self).__init__(methodName)
        
        if webdriver_provider == None:
            self._webdriver = WebDriverSingleton.get_instance()
        else:
            self._webdriver = webdriver

        if screenshot_util == None:
            self._screenshot_util = WebScreenShotUtil
        else:
            self._screenshot_util = screenshot_util 
    ...
def run(self, result=None):
        """
        Overriding the run() method to insert our screenshot handler.
        
        Most of this method is a copy of the TestCase.run() method source.
        """
        orig_result = result
        if result is None:
            result = self.defaultTestResult()
            startTestRun = getattr(result, 'startTestRun', None)
            if startTestRun is not None:
                startTestRun()
            ... more pyunit code ...
                except self.failureException:
                    # Insert our Take Screenshot on test failure code.
                    fname = str(self).replace("(", "").replace(")", "").replace(" ", "_")
                    fmt='%y-%m-%d_%H.%M.%S_.PNG'
                    filename = datetime.datetime.now().strftime(fmt)
                    self._screenshot_util.take_screenshot(self._webdriver, filename)
                    result.addFailure(self, sys.exc_info())
            ... more pyunit code...
                except:
                    # Do the same thing again for errors.
                    fname = str(self).replace("(", "").replace(")", "").replace(" ", "_")
                    fmt='%y-%m-%d_%H.%M.%S_.PNG'
                    filename = datetime.datetime.now().strftime(fmt)
                    self._screenshot_util.take_screenshot(self._webdriver, filename)
                    result.addError(self, sys.exc_info())
            ... more pyunit code ...

...


Now that I have inserted calls to 'take_screenshot' into the try/except blocks that handle test errors, we now will capture screenshot whenever a test extending this test fails. You can use this test case like this:

import unittest
from wtframework.wtf.testobjects.basetests import WTFBaseTest
from wtframework.wtf.web.webdriver import WTF_WEBDRIVER_MANAGER


class TestScreenCaptureOnFail(ScreenCaptureTestCase):
    """"
    These test cases are expected to fail.  They are here to test 
    the screen capture on failure.
    """

    # Comment out decorator to manually test the screen capture.
    @unittest.expectedFailure
    def test_fail(self):
        driver = WTF_WEBDRIVER_MANAGER.new_driver()
        driver.get('http://www.google.com')
        self.fail()
        #Check your /screenshots folder for a screenshot.

There you go, we have a simple method that automatically captures screenshots upon test failure.


Update: 
I have since then incorporated this into WTFramework,
https://github.com/wiredrive/wtframework

In WTFramework, I extended the basic TestCase class to WatchedTestCase, which allows you to register listeners.
https://github.com/wiredrive/wtframework/blob/master/wtframework/wtf/testobjects/testcase.py

This then allows me to register various listeners like my CaptureScreenShotOnErrorTestWatcher
https://github.com/wiredrive/wtframework/blob/master/wtframework/wtf/testobjects/basetests.py