Menu
Seligman Ventures Ltd
  • Home
  • About Us
  • Training
  • Blog
  • Privacy Policy
  • Contact Us
  • Home
  • About Us
  • Training
  • Blog
  • Privacy Policy
  • Contact Us

Test Automation - Page Object Model with Gherkin (Selenium  & C# centered)

3/28/2018

3 Comments

 
Following the popularity of my last post regarding a Test Automation framework for Appium (Java) centered testing, I have had many requests to create one that is more centred around Selenium & C#.  

 As with my previous blog, the purpose of this post is to provide an evolving best practice guidelines for structuring & writing automation frameworks, using Behaviour Driven Development (through cucumber) and good code structure (through Page Object Model pattern):

HIGH-LEVEL STRUCTURE:
Below is a proposed high-level structure for test automation projects. Each blue box represents a collection of classes or feature files:
Picture
  1. Feature files are made up of User Stories & Acceptance Criteria used to drive the tests. They are written in gherkin syntax (Given, When, Then).
  2. Step definitions allow the gherkin syntax to drive the lower level test automation code.
  3. Object Buckets are used to store/reuse data across test steps within the same scenario (e.g. instances of page objects created), using Dependency Injection included with SpecFlow.
  4. Test Data Objects are logical groupings of test data (e.g. a test user class with an email address and password property).
  5. Step definitions communicate with Page Objects, which are representations of the pages/menus within the Website being tested.
  6. Driver Facade class provides a wrapper for Selenium WebDriver allowing additional logging and any other interaction specific code required.

​
Feature files:
 
These represent the User Stories/Acceptance Criteria (similar to business requirements) and are written in ‘business speak’ using Gherkin syntax. 
Picture
Best Practice:

  • A separate feature file should be created for each feature/functional area within the app.
  • ‘Given’ sentences can be written using past tense, ‘Whens’ using present tense and ‘Thens’ using future tense (though some prefer present tense for all (e.g. Given/When reuse becomes easier)
  • The ‘When’ section of a scenario will commonly be only one row long (making the purpose of test easily identifiable)
  • Given/When sections will rarely appear after a Then statement.
  • Scenario titles should not be too long. If more detail is needed add description row(s) underneath the title.
  • Use ‘Given I have navigated to XXXX page’ to get test pre-condition to start on a certain page. Both these statements can be used in the scenario Background.


​Step definitions:
 
Step definitions are the interface between the ‘business speak’ gherkin and the technically coded Page Objects.  They are made up of methods which are called when the higher level Cucumber scenarios are run.

Picture
Best Practice:
 
  • Within the constructor, create/reuse any Object Bucket instances (e.g. Pages class) that are needed within that specific Step Definition file.  This can be done using SpecFlow’s dependency injection system.
  • Use instances of page objects and test data objects within the step definition file to drive your tests. 
  • Each feature file could have its own step definition file
  • Step definition files should contain your asserts.
 
Code Reuse:
 
  • Write your code for a step definition.  If that code is required by other step definitions within the same file, refactor that code into a private method (see example below). If different step definition files also require the same code, refactor it into a ‘CommonSteps’ step definition file.
Picture
​Common Step Definitions file:
 
Create a Common Step Definitions file which can be used for step definitions used across different Feature files. 
This file can also be used to construct the DriverFacade class and create the initial page object (e.g. for the HomePage).  It can also quit the DriverFacade class at the end of tests. This means DriverFacade only needs to be exposed on this one Step Definition file, remaining hidden & passed between the Page Objects on all other Step Definition files (reducing the likelihood of its misuse).
Here is an example implementation (where we have refactored some of the previous steps into this commons steps file):
Picture
Picture
​Object Buckets:
 
These are used to persist data across Step Definitions as a Cucumber scenario runs.
Picture
Best Practice:
  • Buckets will simply be ‘plain old’ classes with properties, allowing data to be stored only for the duration of a single test scenario. For example, we recommend storing page object instances between scenarios (so that the DriverFacade can be hidden from the Step definition layer).
  • Instances of these buckets will be destroyed automatically by Cucumber at the end of each test scenario.
 
 
Test Data Objects:
 
These objects are used to store logical groupings of test data information, for example a Test User with an email and password property.  They allow your test suite to pass around representations of real world objects as single entities, rather than as lots of individual variables (which can get confusing & cumbersome to maintain).
Picture
​Best Practice:
  • Use these ‘plain old’ classes with properties to represent different Test Data that will be used by your tests. For example, a TestUser class might have a default username & password property set in its constructor. You could then pass an instance of testUser into a relevant method: loginPage.loginWithValidUser(testUser);
  • Updates to default values should be made in the Step definition files so the purpose of the test is obvious, e.g:
TestUser user1 = new TestUser();
user1.password = “invalid_password”;
loginPage.loginWithValidUser(user1);
  • Alternatively, you can pass enum values (e.g. TestUserType.Invalid) into the Test Data constructor to achieve the same outcome.
  • Use getters & setters for test data objects (so that controls can be added if needed at a later stage).
 
 
Page Objects:
 
A separate class is created for each page or section (e.g. navigation menu) of the Website. The methods on these classes will drive the Web pages (e.g. Actions like: loginWithAValidUser()) or return information about the state of the website (e.g. Queries like: getUsernameDisplayed()).
Picture
Best Practice:
  • Pass the single instance of DriverFacade into all page objects’ constructor method (see screenshot above).
  • Methods can be functional (e.g. loginWithValidUser) or more specific (e.g. enterEmailAddress). Often functional methods will call specific methods (meaning you maximise the testing potential of the page object and avoid code duplication).
  • Any navigation methods (e.g. GotoLoginPage) should return an instance of the subsequent page object (e.g. LoginPage).  This will allow test writers to navigate through the test suites they are running more easily (since resulting pathway flows will be apparent from the code design). It can also help identify failing tests when, for example, a page object no longer links to a certain page.  Additionally it will mean the FacadeDriver is not exposed to the Step Definition pages (making its misuse less likely).
  • Common navigation menus should form separate page object classes. Any page object (e.g. HomePage) that has the navigation menu should then have the navigation menu page object as a property of itself (using composition rather than inheritance).
  • Page Objects should not make assertions, except optionally in the constructor to check the page is displayed (which can help make sure the correct page is correctly loaded before proceeding).
  • If a single flow in the application can lead to different pages being presented, then create a separate method for each journey. E.g: loginWithValidUser (returns welcome page) vs loginWithInvalidUser (returns error page).
  • When using NavigationControllers, a single page’s back button might return you to different pages. When this happens you can create multiple methods e.g.  'Back to homepage', 'Back to Account Page' that call the same private method which actually selects the ‘back’ button.
  • For websites, if name and id are not available as properties of objects, use CSS if possible (faster & more consistent than xpath). For Appium xpath is used instead of CSS.
  • Page Objects should not use getters & setters, since the page objects are encapsulating the UI and so will get and set any values using the UI (not internal class properties).

 
DriverFacade:
 
This is simply a wrapper class between our test automation code and the third party test automation framework (e.g. Selenium WebDriver).  
Picture
Best Practice:
  • The page objects should interact with FacadeDriver rather than directly with WebDriver.  This will allow far more control over test runs (e.g. logging), as well as providing the ability to switch in and out of different automation tools if required.
  • The DriverFacade should be the only class in the test automation framework that interacts directly with Selenium / Appium.
  • Set the implicit wait to be a low value (e.g. 5 or 10 seconds), and then wait for elements with a longer time out when required. This will help ensure tests are faster to run.
 
Folder Structure:
 
Below is an example of a folder structure you could use for implementing the aforementioned process:
Picture
Future additions to this process:
 
  • Adding selenium’s Page Factory.
  • Process for writing Gherkin.  Perhaps agree scenario titles & relative priorities as a group across Dev, Test & Business (to ensure nothing is missed), but then write individual steps as a tester (to ensure correct wording & ordering of steps is achieved).
3 Comments

    Author

    Seligman Ventures Ltd provide Software Testing & Development Services

    Archives

    March 2018
    December 2017
    February 2014
    January 2014

    Categories

    All

    RSS Feed

Powered by Create your own unique website with customizable templates.