1

I want to get a better understanding about Selenium Framework. Is it true that "framework" in this context is not the same as DataDriven/KeyworkDriven/Hybrid framework? In other words, these three framework are only a piece of the puzzle when you design framework for the entire project and where can I see code example on the web?

  • 7
    Selenium is NOT framework - it is a library: see sqa.stackexchange.com/questions/26079/… – Peter M. - stands for Monica Mar 15 '17 at 17:11
  • 1
    I usually custom design a framework based on the test users and application needs. The framework itself is all custom code to abstract selenium usage to an easier to use level for the end user. The goal is to decrease code duplication and customize common functions to match the application. Then end users can easily find what they need without having to write all the nuances out like wait for element and such depending on the needs. – mutt Mar 15 '17 at 19:26
  • @mutt Thanks a lot. I have experience creating POM and running test cases, but need to beef up my knowledge when it comes to designing a framework. In my view, a framework should include not only classes with all the test cases that you run, but also where you create classes that put info in log files, make snapshots of failed test cases, connect with DB and so on. Another user provided useful links in his answer, but it does not cover all the above questions. Where can I find such info? I think it is better to reply to this as an answer, not as a comment. Thanks again – Richardson Mar 15 '17 at 23:36
  • Logging depends in the language you select. A popular option is log4j. I use NUnit so I just write to Console and my CI saves all output. My Results.xml (also NUnit) file is parsed by a plugin for my CI tool after the test run finishes. Screenshots are in the Selenium Support library. – kirbycope Mar 15 '17 at 23:54
  • @kirbycope I used java at my last project, so the first link i opened was very useful. However, can you help me to get more info on what I asked above. How all these classes are grouped together in a framework and what is hirerachial relationship between them? In other words, which should extend which? – Richardson Mar 16 '17 at 0:08
2

As Peter commented, Selenium is a library not a framework. You can use the Selenium library to build your own framework.

I created a framework that uses *Unit(JUnit for Java and NUnit C#), Selenium 3, the Page Object Model, and Grid. Each link (below) has a step-by-step guide and a GitHub repo.

1

So in designing a framework I usually do the following basic things and then custom tweak after that to match the user and app specific needs.

  1. Create a singleton pattern for driver instantiation which sets the needed capabilities including consistent resolution (helps alot with web pages that are supposed to scale to multiple devices) This can be parametized in a config file or passed variables from the test itself or statically set. Browser type may also fit in there. The root of the class is similar and if you utilize Grid you might start with RemoteWebDriver and then cast to the extenders of Firefox/IE/Chrome/etc...up to you though based on your specific setup
  2. Second I abstract the element selection and the actions from the test themselves and usually utilize enums for the user to select the desired functionality via parameters and then pass in the needed variables for the map and any additional items. This usually helps non-programming people understand the logic via reading text so then they only have to learn how to organize the text in the code structure within the test. Intellisense also is great as it will show the enum and function options when you type the . which helps self document the guide.
  3. I abstract the results and or errors depending on needs. If you want more info than a selenium error or a unit test "fail/pass" you need to do this. You catch errors and results and log them or throw them while including useful information such as map string and element attempted action etc... this points someone using the framework to the exact problem instead of just "fail and look for it".
  4. I put all the bells and whistles to whoever it's needed for and how much the budget supports. I had the pleasure of doing a fully customized one with a web interface as well as CI connection with distributed testers that spit out nice html results and had persistent tests that reported all steps as pass/fail instead of just the test as a whole. This was really fun, and not likely to be able to do one on that level again, but it's up to you and the needs and abilities of the individuals.

I hope this helps some. Post again if you run into specific issues in trying to implement something. Good luck!

  • Thanx a lot. All the info is very valuable. I think that I also found a link that can help me. Please look here: seleniumeasy.com/selenium-tutorials/… – Richardson Mar 16 '17 at 16:56
  • Would you agree that if you at least from Project Folder Structure downwards, everything is pretty close to building a real framework. Specific questions: I think that saving all the info in a property file is redundant. You can save all that in a separate Java file as String variables and this way you wont need to resort to Properties class with all its methods. Do you agree that this approach may be right? – Richardson Mar 16 '17 at 17:03
  • It just depends on who is managing it. I separate to config files and variable passing to eliminate the end users need to read code. They get an easily formatted file or a intellisense prompt to include a variable that is english understandable. The link breaks down some good things too, it really depends on your target user set. I tend to gear mine for non-programmers, which has more encapsulation and abstraction than other semi-programmer like approaches. The extra layers I mention are for ease of use so that manual testers can write automation without too much headache. – mutt Mar 16 '17 at 18:31
  • On another note I highly recommend separating the ui maps behind keywords. This avoids duplicate ui maps and allows for global re-use where the map is identical based on the unique keyword or name used. Externalizing that in a central database with a user interface to manage and then pulling it in real time on test execution allows easier maintenance as well. – mutt Mar 16 '17 at 18:37
0

I have Designed and Developed the Selenium Hybrid Framework

Maven skeleton structure:

eap.base   --Package Name
filename:Retry.java
package eap.base;
import org.testng.IRetryAnalyzer;
import org.testng.ITestResult;

public class Retry implements IRetryAnalyzer {
    private int retryCount = 0;
    private int maxRetryCount = 0;

    public boolean retry(ITestResult result) {
        if (retryCount < maxRetryCount) {
            System.out.println("Retrying test " + result.getName() + " with status "
                    + getResultStatusName(result.getStatus()) + " for the " + (retryCount + 1) + " time(s).");
            retryCount++;
            return true;
        }
        return false;
    }

    public String getResultStatusName(int status) {
        String resultName = null;
        if (status == 1)
            resultName = "SUCCESS";
        if (status == 2)
            resultName = "FAILURE";
        if (status == 3)
            resultName = "SKIP";
        return resultName;
    }
}

Filename=RetryListener

package eap.base;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

import org.testng.IAnnotationTransformer;
import org.testng.IRetryAnalyzer;
import org.testng.annotations.ITestAnnotation;

public class RetryListener implements IAnnotationTransformer {

    @SuppressWarnings("rawtypes")
    @Override
    public void transform(ITestAnnotation testannotation, Class testClass, Constructor testConstructor,
            Method testMethod) {
        IRetryAnalyzer retry = testannotation.getRetryAnalyzer();
        if (retry == null) {
            testannotation.setRetryAnalyzer(Retry.class);
        }
    }
}

FileName=setup

package eap.base;

import org.apache.poi.xssf.usermodel.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import org.apache.logging.log4j.Logger;
import org.apache.commons.io.FileUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.CellType;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;
import org.openqa.selenium.support.ui.ExpectedCondition;
import org.openqa.selenium.support.ui.WebDriverWait;
import org.testng.ITestResult;
import org.testng.annotations.AfterClass;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.AfterSuite;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Parameters;
import com.assertthat.selenium_shutterbug.core.Shutterbug;
import com.relevantcodes.extentreports.ExtentReports;
import com.relevantcodes.extentreports.ExtentTest;
import com.relevantcodes.extentreports.LogStatus;

public class setup {
    private static WebDriver driver;
    static String resourcePath = "./src/test/resources/";
    protected static ExtentTest reportLog;
    ExtentReports extent = new ExtentReports("./ExtentReport.html", true);
    protected static Logger logger = LogManager.getLogger();
    static int PAGELOADWAIT = 60;
    static int PAGELOAD = 3000;

    public WebDriver getDriver() {
        return driver;
    }

    private void setDriver(String browserType, String appURL) {
        if (browserType.equals("chrome")) {
            driver = initChromeDriver(appURL);
        } else {
            logger.info("Invalid selection");
        }
    }

    private static WebDriver initChromeDriver(String appURL) {
        System.setProperty("webdriver.chrome.driver", resourcePath + "driver/" + "chromedriver.exe");
        ChromeOptions options = new ChromeOptions();
        options.addArguments("--start-maximized");
        options.addArguments("disable-infobars");
        WebDriver driver = new ChromeDriver(options);
        driver.get(appURL);
        return driver;
    }

    public Map<String, String> loadObject() {
        Properties prop = new Properties();
        Map<String, String> map = new HashMap<String, String>();
        String objFileName = className();
        try {
            FileInputStream inputStream = new FileInputStream(resourcePath + "obj/" + objFileName + ".properties");
            prop.load(inputStream);
            inputStream.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        for (final Entry<Object, Object> entry : prop.entrySet()) {
            map.put((String) entry.getKey(), (String) entry.getValue());
        }
        return map;
    }

    public static void hardWait(int waitValue) {
        try {
            Thread.sleep(waitValue);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static void waitForJStoLoad(WebDriver driver) {
        hardWait(PAGELOAD); // need to remove
        WebDriverWait wait = new WebDriverWait(driver, PAGELOADWAIT);
        wait.until(new ExpectedCondition<Boolean>() {
            public Boolean apply(WebDriver driver) {
                return ((JavascriptExecutor) driver).executeScript("return document.readyState").equals("complete");
            }
        });
    }

    public static String printDate() {
        String PATTERN = "yyyyMMddhhmmss";
        SimpleDateFormat dateFormat = new SimpleDateFormat();
        dateFormat.applyPattern(PATTERN);
        String myDate = dateFormat.format(Calendar.getInstance().getTime());
        return myDate;
    }

    public static void backUpReport() {
        File srcFile = new File("./ExtentReport.html");
        File dstnFile = new File("./old-reports/ExtentReport" + printDate() + ".html");
        try {
            FileUtils.copyFile(srcFile, dstnFile);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public ArrayList<LinkedHashMap<String, String>> readDataFileBySheet(String excelName, String sheetName) {
        LinkedHashMap<String, String> row = new LinkedHashMap<String, String>();
        ArrayList<LinkedHashMap<String, String>> dataSet = new ArrayList<LinkedHashMap<String, String>>();
        InputStream file = null;
        XSSFWorkbook workbook = null;
        XSSFSheet sheet = null;
        try {
            file = new FileInputStream(resourcePath + "data/" + excelName + ".xlsx");
            workbook = new XSSFWorkbook(file);
            sheet = workbook.getSheet(sheetName);
            int noOfRows = sheet.getLastRowNum();
            String[] testMethodList = new String[noOfRows + 1];
            XSSFRow headerRow = null;
            XSSFRow valueRow = null;
            for (int i = 0; i <= noOfRows; i++) {
                testMethodList[i] = sheet.getRow(i).getCell(0).getStringCellValue();
                row = new LinkedHashMap<String, String>();
                headerRow = ((XSSFSheet) sheet).getRow(0);
                valueRow = ((XSSFSheet) sheet).getRow(i);
                for (int j = 0; j < headerRow.getPhysicalNumberOfCells(); j++) {
                    String cellValue = null;
                    if (valueRow.getCell(j).getCellTypeEnum() == CellType.STRING) {
                        cellValue = valueRow.getCell(j).getStringCellValue().toString();
                    } else if (valueRow.getCell(j).getCellTypeEnum() == CellType.NUMERIC) {
                        cellValue = String.valueOf((valueRow.getCell(j).getNumericCellValue()));
                    } else {
                        logger.info("Data error");
                    }
                    row.put(headerRow.getCell(j).getStringCellValue().toString(), cellValue);
                }
                dataSet.add(row);
            }
            workbook.close();
            file.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return dataSet;
    }

    public List<String> myData(String excelName) {
        InputStream file = null;
        XSSFWorkbook workbook = null;
        List<String> sheetNames = new ArrayList<String>();
        try {
            file = new FileInputStream(resourcePath + "data/" + excelName + ".xlsx");
            workbook = new XSSFWorkbook(file);
            for (int i = 0; i < workbook.getNumberOfSheets(); i++) {
                sheetNames.add(workbook.getSheetName(i));
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        ArrayList<LinkedHashMap<String, String>> nu = null;
        ArrayList<LinkedHashMap<String, String>> a = null;
        for (String sheet : sheetNames) {
            nu = readDataFileBySheet(excelName, sheet); // change here 
        }
        return sheetNames;
    }

    public ArrayList<LinkedHashMap<String, String>> readDataFile(String methodName, String sheetName) {
        String excelName = className();
        LinkedHashMap<String, String> row = new LinkedHashMap<String, String>();
        ArrayList<LinkedHashMap<String, String>> dataSet = new ArrayList<LinkedHashMap<String, String>>();
        InputStream file = null;
        XSSFWorkbook workbook = null;
        XSSFSheet sheet = null;
        try {
            file = new FileInputStream(resourcePath + "data/" + excelName + ".xlsx");

            workbook = new XSSFWorkbook(file);
            sheet = workbook.getSheet(sheetName);
            int noOfRows = sheet.getLastRowNum();
            String[] testMethodList = new String[noOfRows + 1];
            XSSFRow headerRow = null;
            XSSFRow valueRow = null;
            for (int i = 0; i <= noOfRows; i++) {
                testMethodList[i] = sheet.getRow(i).getCell(0).getStringCellValue();
                if (testMethodList[i].equals(methodName)) {
                    row = new LinkedHashMap<String, String>();
                    headerRow = ((XSSFSheet) sheet).getRow(0);
                    valueRow = ((XSSFSheet) sheet).getRow(i);
                    for (int j = 0; j < headerRow.getPhysicalNumberOfCells(); j++) {
                        String cellValue = null;
                        if (valueRow.getCell(j).getCellTypeEnum() == CellType.STRING) {
                            cellValue = valueRow.getCell(j).getStringCellValue().toString();
                        } else if (valueRow.getCell(j).getCellTypeEnum() == CellType.NUMERIC) {
                            cellValue = String.valueOf((valueRow.getCell(j).getNumericCellValue()));
                            // cellValue = cellValue.substring(0,
                            // cellValue.length() - 2); // if decimal
                        } else {
                            logger.info("Data error");
                        }
                        row.put(headerRow.getCell(j).getStringCellValue().toString(), cellValue);
                    }
                    dataSet.add(row);
                }
            }
            workbook.close();
            file.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return dataSet;
    }

    public ArrayList<LinkedHashMap<String, String>> iterator(String methodName, String sheetName) {
        String excelName = className();
        LinkedHashMap<String, String> row = new LinkedHashMap<String, String>();
        ArrayList<LinkedHashMap<String, String>> dataSet = new ArrayList<LinkedHashMap<String, String>>();
        try {
            FileInputStream file = new FileInputStream(resourcePath + "data/" + excelName + ".xls");
            HSSFWorkbook workbook = new HSSFWorkbook(file);
            HSSFSheet sheet = workbook.getSheet(sheetName);
            int noOfRows = sheet.getLastRowNum();
            String[] testMethodList = new String[noOfRows + 1];
            HSSFRow headerRow = null;
            HSSFRow valueRow = null;

            for (int i = 0; i <= noOfRows; i++) {
                testMethodList[i] = sheet.getRow(i).getCell(0).getStringCellValue();
                if (testMethodList[i].equals(methodName)) {
                    row = new LinkedHashMap<String, String>();
                    headerRow = ((HSSFSheet) sheet).getRow(0);
                    valueRow = ((HSSFSheet) sheet).getRow(i);
                    for (int j = 0; j < headerRow.getPhysicalNumberOfCells(); j++) {
                        row.put(headerRow.getCell(j).getStringCellValue().toString(),
                                valueRow.getCell(j).getStringCellValue().toString());
                    }
                    dataSet.add(row);
                }
            }
            workbook.close();
            file.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return dataSet;
    }

    public static String screenCapture(WebDriver driver, String message) {
        waitForJStoLoad(driver);
        long unixTimestamp = Instant.now().getEpochSecond();
        String screenFile = "screencapture_" + unixTimestamp;
        try {
            Shutterbug.shootPage(driver).withTitle(message).withName(screenFile).save();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return screenFile;
    }

    /*
     * public String getscreenshot(WebDriver driver) { waitForJStoLoad(driver);
     * long unixTimestamp = Instant.now().getEpochSecond(); File scrFile =
     * ((TakesScreenshot) driver).getScreenshotAs(OutputType.FILE); String
     * screenFile = null; try { screenFile = "./output/screencapture" +
     * unixTimestamp + ".png"; FileUtils.copyFile(scrFile, new
     * File(screenFile)); } catch (IOException e) { e.printStackTrace(); }
     * return screenFile; }
     */

    public static void reportTest(WebDriver driver, String status, String message) {
        String screenShotPath = null;
        if (status.equals("PASS")) {
            reportLog.log(LogStatus.PASS, message);
        } else if (status.equals("FAIL")) {
            reportLog.log(LogStatus.FAIL, message);
            screenShotPath = screenCapture(driver, message);
            // screenShotPath = getscreenshot(driver);
            reportLog.log(LogStatus.FAIL,
                    "Find Snapshot below: " + reportLog.addScreenCapture("./screenshots/" + screenShotPath + ".png"));
        } else if (status.equals("INFO")) {
            reportLog.log(LogStatus.INFO, message);
        }
    }

    @Parameters({ "browserType", "appURL" })
    @BeforeClass
    public void initialize(String browserType, String appURL) {
        try {
            setDriver(browserType, appURL);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @BeforeClass
    public String className() {
        String classPath = this.getClass().getName();
        String className = (classPath.substring(classPath.lastIndexOf(".") + 1).replace("Page", "")).replace("Test",
                "");
        return className;
    }

    @BeforeTest
    public void startReport() {
        try {
            extent.addSystemInfo("Host Name", "HostName").addSystemInfo("Environment", "Automation Env")
                    .addSystemInfo("User Name", "Author");
            extent.loadConfig(new File(resourcePath + "config/" + "extent.xml"));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @BeforeMethod
    public void startReportheader(Method m) {
        reportLog = extent.startTest(m.getName());
    }

    @AfterMethod
    public void getResult(ITestResult result) 
    {
        try {
            if (result.getStatus() == ITestResult.FAILURE) {
                reportLog.log(LogStatus.FAIL, "Test Case Failed");
                reportLog.log(LogStatus.FAIL, result.getThrowable());
            } else if (result.getStatus() == ITestResult.SKIP) {
                reportLog.log(LogStatus.SKIP, "Test Case Skipped");
            } else if (result.getStatus() == ITestResult.SUCCESS) {
                reportLog.log(LogStatus.INFO, "Test Case Execution Complete");
            }
            extent.endTest(reportLog);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @AfterMethod
    public void endReportheader(Method m) {
        extent.flush();
    }

    @AfterSuite
    public void endReport() {
        extent.close();
        backUpReport();// backup Old report
    }

    /*@AfterClass
    public void tearDown() {
        driver.quit();
    }*/

}
  • What are you doing here? Please edit your answer with single response. It seems that you replied multiple answers for a single question. Please make sure the exact one which is most suitable & delete others or combine it in single answer. Otherwise someone will deletes/down votes or flags to your all the answers. – Bharat Mane Feb 2 '18 at 6:57
  • Please edit your post to explain how your custom framework answers the OP's question. – Kate Paulk Feb 2 '18 at 15:51

Not the answer you're looking for? Browse other questions tagged or ask your own question.