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:
Feature files: These represent the User Stories/Acceptance Criteria (similar to business requirements) and are written in ‘business speak’ using Gherkin syntax. Best Practice:
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. Best Practice:
Code Reuse:
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): Object Buckets: These are used to persist data across Step Definitions as a Cucumber scenario runs. Best Practice:
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). Best Practice:
user1.password = “invalid_password”; loginPage.loginWithValidUser(user1);
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()). Best Practice:
DriverFacade: This is simply a wrapper class between our test automation code and the third party test automation framework (e.g. Selenium WebDriver). Best Practice:
Folder Structure: Below is an example of a folder structure you could use for implementing the aforementioned process: Future additions to this process:
2 Comments
Having done both Development & Test Automation for over 10 years now, I realised the focus of this blog has been primarily on development. Therefore, I thought it would be interesting to cover some Test Automation. Gherkin (the human readable coding language used by Cucumber) is becoming increasingly popular as more companies embrace Behaviour Driven Development (BDD). The Page Object Model is an equally important tool in the test automator's arsenal and so I was keen to show how these two structures can work together to provide a well structured, modular & easily updatable test automation framework. Please do leave any comments below, since I plan to update this framework approach as suggestions come in. In summary, 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:
FEATURE FILES: These represent the business requirements and are written in ‘business speak’ using Gherkin syntax Best Practice:
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. Best Practice:
Code Reuse:
OBJECT BUCKETS: These are used to persist data across Gherkin test steps as a Cucumber scenario runs. The data will be wiped at the end of each scenario. Rather than creating new instances of pages manually at the start of each step definition method, this way means you don't have to expose the Driver instance on every Step definition page (meaning lazy use of e.g. driver.FindElementBy().Click() at the step definition level is not possible) Best Practice:
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). Best Practice:
user1.password = “invalid_password”; loginPage.loginWithValidUser(user1);
PAGE OBJECTS: A separate class is created for each page or navigation menu of the Application Under Test (AUT). The methods on these classes will drive the AUT pages (e.g. Actions like: loginWithAValidUser) or return information about the state of the app (e.g.Queries like: getUsernameDisplayed). Best Practice:
APPDRIVER: This is simply a wrapper class between our test automation code and the third party test automation framework (e.g. Selenium WebDriver). Best Practice:
FOLDER STRUCTURE: Below is an example of a folder structure you could use for implementing the aforementioned process: DataObjects:
This folder can contain Test Data Objects and any other object representations you may need for your test suite. Facades: A façade class is one that provides a simplified gateway to more complex classes. AppDriver is one example of a façade but others can be stored in this folder. Utilities: The Utilities folder will contain classes that use static methods to provide time saving actions, such as logging. The remaining folder titles, in the screenshot above, have been covered earlier in this document. ADDITIONS YOU COULD MAKE:
If you are new to coding you might be wondering about best techniques when designing classes. Here are some thoughts on the matter, deemed from a highly talented Developer I work with. The example uses a simple calculator class that takes 2 numbers & performs a calculation on them (e.g. add, subract
RULE 1: Pass values in via a constructor if they are mandatory AND will not generally change Calculator calc = new Calculator(number1, number2, transactionType); double result = calc.GetResult(); The trouble with the code above is you would want to pass in different numbers and calculation types. So in effect your class goes from being a flexible ‘calculator’ class to an inflexible ‘singleCalculation’ class. RULE 2: Don’t allow users to misuse your class or interface Calculator calc = new Calculator(); calc.number1 = 5; calc.number2 = 7; calc.transactionType = TransactionType.Multiply double result = calc.GetResult(); The trouble with the code above is that if the user of your code forgets to set calc.transactionType (which could be easily done since it doesn’t get passed into the constructor or the GetResult() method) then the class will error RULE 3: Save memory Calculator calc = new Calculator(); double result1 = calc.GetResult(5, 7, TransactionType.Multiply); double result2 = calc.GetResult(result1, 7, TransactionType.Multiply); Passing the parameters into GetResults mean we pass both Rules 1 and 2 above and also save on memory since we only have to construct one instance of calculator class.
|
AuthorSeligman Ventures Ltd provide Software Testing & Development Services Archives
March 2018
Categories |