Friday, September 19, 2014

Dimuthu De Lanerolle

How to add test ui modules to products

Abstract

This article will mainly focus on core areas engineers should know when adding integration UI tests related modules to products in-order to run with WSO2 Test Automation Framework. If you are familiar with Selenium you can directly start writing UI tests after adding these UI modules to your product.

Table of contents

Introduction
Structure of the implementation
Dependency management for UI tests
Scopes
test
compile
Plugin and Configuration management for UI tests
maven-surefire-plugin
maven-clean-plugin
maven-dependency-plugin
maven-jar-plugin
maven-resources-plugin
Classes
Writing the test case
Execution of tests
Summary


Introduction

Wso2 TAF  is an automation framework that performs equally in all stages of the deployment lifecycle. To refer more on Wso2 TAF please refer WSO2 TAF documentation.
Our objective is to present a comprehensive guidance on adding tests-UI related modules to Wso2 products and a step-by-step guide describing the execution of tests.
Note: Readers of this article are expected to be familiar themselves with TestNG and Selenium for writing UI tests. (Please refer TestNG documentation and Selenium http://www.seleniumhq.org/docs/)

Structure of the implementation

In order to begin with our implementation we have to define a structure to our project.

For the demonstration purposes we will now consider a use case of introducing UI modules for Wso2 BAM product. To learn more on Wso2 BAM please refer https://docs.wso2.com/display/BAM241/WSO2+Business+Activity+Monitor+Documentation

You can clone the WSO2 BAM product source from the following github HTTP clone URL
https://github.com/wso2-dev/product-bam

After cloning the source we need add relevant modules to the project structure. Navigate to …./product-bam/modules/integration module. If not exist you need to add tests-common module to the integration module. Now navigate to created tests-common module and we need to create ui-pages module inside the tests-common module.

Moreover we need to add a new tests-ui-integration module for the purpose of writing UI tests in our project. To do so navigate back to …./product-bam/modules/integration module and create tests-ui-integration maven module.

Finally our project structure should look similar to below graphical representation.



Screenshot from 2014-09-16 19:24:34.png


tests-common

This module is used to add useful custom common utilities in helping writing our tests. 

ui-pages

This module can be used to store page object classes that we can use inside our tests. For convenience you can create separate sub-modules (eg: home)  for each page object type and store relevant page objects classes (eg : HomePage.java)  inside these sub-modules.

tests-ui-integration

Our test classes can be written inside this module.

Dependency management for UI tests

Maven dependency management is one of the key features of Maven and for our exercise also we need to identify required maven dependencies for the project. We will now list-down basic key maven dependencies you need to add to your relevant pom.xml file/s. We will travel through each module and describe the necessary maven dependencies need to be added to each pom.xml file.

Note the groupId, artifactId and versions of each pom.xml files. You can replace relevant values in accordance with your module structure. In-order to adhere into the best practise we will define all required dependencies in the root pom.xml ( product-bam level pom.xml file) with the versions ( note the properties tags which defines actual versions for each dependency type) enabling other pom.xml files exists below the root pom.xml to derive the dependency versions automatically.

product-bam pom.xml

  ………………..  

    </dependencies>
      <dependency>
                <groupId>org.wso2.carbon.automation</groupId>
                <artifactId>org.wso2.carbon.automation.engine</artifactId>
                <version>${test.framework.version}</version>
                <scope>test</scope>
            </dependency>
            <dependency>
                <groupId>org.wso2.carbon.automation</groupId>
                <artifactId>org.wso2.carbon.automation.extensions</artifactId>
                <version>${test.framework.version}</version>
                <scope>test</scope>
            </dependency>
            <dependency>
                <groupId>org.wso2.carbon.automation</groupId>
                <artifactId>org.wso2.carbon.automation.test.utils</artifactId>
                <version>${test.framework.version}</version>
                <scope>test</scope>
            </dependency>
            <dependency>
                <groupId>org.wso2.bam</groupId>
                <artifactId>org.wso2.bam.integration.ui.pages</artifactId>
                <version>${bam.version}</version>
                <scope>test</scope>
            </dependency>
            <dependency>
                <groupId>org.testng</groupId>
                <artifactId>testng</artifactId>
                <version>${testng.version}</version>
            </dependency>
            <dependency>
                <groupId>org.wso2.carbon</groupId>
                <artifactId>org.wso2.carbon.integration.common.admin.client</artifactId>
                <version>${carbon.version}</version>
                <scope>test</scope>
            </dependency>
            <dependency>
                <groupId>org.wso2.carbon</groupId>
                <artifactId>org.wso2.carbon.integration.common.extensions</artifactId>
                <version>${carbon.version}</version>
                <scope>test</scope>
            </dependency>
        </dependencies>


 <properties>
          <carbon.version>4.3.0-SNAPSHOT</carbon.version>
           <test.framework.version>4.3.1-SNAPSHOT</test.framework.version>
          <bam.version>2.5.0-SNAPSHOT</bam.version>
          <testng.version>6.8</testng.version>
</properties>
…………...

tests-common pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>org.wso2.bam</groupId>
    <artifactId>bam-integration-tests-common</artifactId>
    <packaging>pom</packaging>
    <version>2.5.0-SNAPSHOT</version>
    <name>WSO2 BAM Server Integration Test Common</name>

    <modules>
        <module>ui-pages</module>
    </modules>
</project>


ui-pages pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

    <parent>
        <groupId>org.wso2.bam</groupId>
        <artifactId>bam-integration-parent</artifactId>
        <version>2.5.0-SNAPSHOT</version>
        <relativePath>../../pom.xml</relativePath>
    </parent>

    <modelVersion>4.0.0</modelVersion>
    <name>WSO2 BAM - Integration Test UI Module</name>
    <groupId>org.wso2.bam</groupId>
    <artifactId>org.wso2.bam.integration.ui.pages</artifactId>

    <dependencies>
        <dependency>
            <groupId>org.wso2.carbon.automation</groupId>
            <artifactId>org.wso2.carbon.automation.extensions</artifactId>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>org.testng</groupId>
            <artifactId>testng</artifactId>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>org.wso2.carbon.automation</groupId>
            <artifactId>org.wso2.carbon.automation.engine</artifactId>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>org.wso2.carbon.automation</groupId>
            <artifactId>org.wso2.carbon.automation.test.utils</artifactId>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>org.wso2.carbon</groupId>
            <artifactId>org.wso2.carbon.integration.common.admin.client</artifactId>
            <scope>compile</scope>
        </dependency>
    </dependencies>
</project>



tests-ui-integration pom.xml

………
  <?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

    <parent>
        <groupId>org.wso2.bam</groupId>
        <artifactId>bam-integration-parent</artifactId>
        <version>2.5.0-SNAPSHOT</version>
        <relativePath>../pom.xml</relativePath>
    </parent>

    <modelVersion>4.0.0</modelVersion>
    <name>BAM Server Integration test UI module</name>
    <artifactId>org.wso2.bam.ui.integration.test</artifactId>
    <packaging>jar</packaging>

    <build>
        <plugins>
            <plugin>
                <artifactId>maven-surefire-plugin</artifactId>
                <inherited>false</inherited>
                <version>2.12.3</version>
                <configuration>
                    <!-- <argLine>-Xmx1024m -XX:PermSize=256m -XX:MaxPermSize=512m -Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5003</argLine>-->
                    <argLine>-Xmx1024m -XX:PermSize=256m -XX:MaxPermSize=512m</argLine>
                    <suiteXmlFiles>
                        <suiteXmlFile>src/test/resources/testng.xml</suiteXmlFile>
                    </suiteXmlFiles>

                    <skipTests>${skipUiTests}</skipTests>

                    <systemProperties>
                        <property>
                            <name>maven.test.haltafterfailure</name>
                            <value>false</value>
                        </property>
                        <property>
                            <name>carbon.zip</name>
                            <value>
                                ${basedir}/../../distribution/target/wso2bam-${project.version}.zip
                            </value>
                        </property>
                        <property>
                            <name>samples.dir</name>
                            <value>${basedir}/../../../samples/product</value>
                        </property>
                        <property>
                            <name>framework.resource.location</name>
                            <value>
                                ${basedir}/src/test/resources/
                            </value>
                        </property>
                        <property>
                            <name>server.list</name>
                            <value>
                                BAM
                            </value>
                        </property>
                        <property>
                            <name>usedefaultlisteners</name>
                            <value>false</value>
                        </property>


                        <sec.verifier.dir>${basedir}/target/security-verifier/</sec.verifier.dir>
                        <emma.home>${basedir}/target/emma</emma.home>
                        <instr.file>${basedir}/src/test/resources/instrumentation.txt</instr.file>
                        <filters.file>${basedir}/src/test/resources/filters.txt</filters.file>
                        <emma.output>${basedir}/target/emma</emma.output>
                    </systemProperties>

                    <workingDirectory>${basedir}/target</workingDirectory>
                </configuration>
            </plugin>

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-clean-plugin</artifactId>
                <version>2.5</version>
                <configuration>
                    <filesets>
                        <fileset>
                            <directory>${basedir}/src/test/resources/client/modules</directory>
                            <includes>
                                <include>**/*.mar</include>
                            </includes>
                            <followSymlinks>false</followSymlinks>
                        </fileset>
                    </filesets>
                </configuration>
            </plugin>

            <plugin>
                <artifactId>maven-dependency-plugin</artifactId>
                <executions>

                    <execution>
                        <id>copy-emma-dependencies</id>
                        <phase>compile</phase>
                        <goals>
                            <goal>copy-dependencies</goal>
                        </goals>
                        <configuration>
                            <outputDirectory>${project.build.directory}/emma</outputDirectory>
                            <includeTypes>jar</includeTypes>
                            <includeArtifactIds>emma
                            </includeArtifactIds>
                        </configuration>
                    </execution>
                    <execution>
                        <id>copy-jar-dependencies</id>
                        <phase>compile</phase>
                        <goals>
                            <goal>copy-dependencies</goal>
                        </goals>
                        <configuration>
                            <outputDirectory>${basedir}/src/test/resources/artifacts/BAM/jar
                            </outputDirectory>
                            <includeTypes>jar</includeTypes>
                            <includeArtifactIds>mysql-connector-java
                            </includeArtifactIds>
                            <excludeTransitive>true</excludeTransitive>
                        </configuration>
                    </execution>
                    <execution>
                        <id>copy-secVerifier</id>
                        <phase>compile</phase>
                        <goals>
                            <goal>copy-dependencies</goal>
                        </goals>
                        <configuration>
                            <outputDirectory>${basedir}/target/security-verifier</outputDirectory>
                            <includeTypes>aar</includeTypes>
                            <includeArtifactIds>SecVerifier</includeArtifactIds>
                            <stripVersion>true</stripVersion>
                        </configuration>
                    </execution>

                    <execution>
                        <id>unpack-mar-jks</id>
                        <phase>compile</phase>
                        <goals>
                            <goal>unpack</goal>
                        </goals>
                        <configuration>
                            <artifactItems>
                                <artifactItem>
                                    <groupId>org.wso2.bam</groupId>
                                    <artifactId>wso2bam</artifactId>
                                    <version>${project.version}</version>
                                    <type>zip</type>
                                    <overWrite>true</overWrite>
                                    <outputDirectory>${basedir}/target/tobeCopied/</outputDirectory>
                                    <includes>**/*.jks,**/*.mar</includes>
                                </artifactItem>
                            </artifactItems>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-jar-plugin</artifactId>
                <version>2.4</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>test-jar</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>

            <plugin>
                <artifactId>maven-resources-plugin</artifactId>
                <version>2.6</version>
                <executions>
                    <execution>
                        <id>copy-resources-jks</id>
                        <phase>compile</phase>
                        <goals>
                            <goal>copy-resources</goal>
                        </goals>
                        <configuration>
                            <outputDirectory>${basedir}/src/test/resources/keystores/products
                            </outputDirectory>
                            <resources>
                                <resource>
                                    <directory>
                                        ${basedir}/target/tobeCopied/wso2bam-${project.version}/repository/resources/security/
                                    </directory>
                                    <includes>
                                        <include>**/*.jks</include>
                                    </includes>
                                </resource>
                            </resources>
                        </configuration>
                    </execution>
                    <execution>
                        <id>copy-resources-mar</id>
                        <phase>compile</phase>
                        <goals>
                            <goal>copy-resources</goal>
                        </goals>
                        <configuration>
                            <outputDirectory>${basedir}/src/test/resources/client/modules
                            </outputDirectory>
                            <resources>
                                <resource>
                                    <directory>
                                        ${basedir}/target/tobeCopied/wso2bam-${project.version}/repository/deployment/client/modules
                                    </directory>
                                    <includes>
                                        <include>**/*.mar</include>
                                    </includes>
                                </resource>
                            </resources>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

    <dependencies>

        <dependency>
            <groupId>org.wso2.carbon.automation</groupId>
            <artifactId>org.wso2.carbon.automation.engine</artifactId>
        </dependency>
        <dependency>
            <groupId>org.wso2.carbon.automation</groupId>
            <artifactId>org.wso2.carbon.automation.test.utils</artifactId>
        </dependency>
        <dependency>
            <groupId>org.wso2.carbon</groupId>
            <artifactId>org.wso2.carbon.integration.common.extensions</artifactId>
        </dependency>
        <dependency>
            <groupId>org.wso2.bam</groupId>
            <artifactId>org.wso2.bam.integration.ui.pages</artifactId>
            <scope>compile</scope>
        </dependency>

    </dependencies>

    <properties>
        <skipUiTests>true</skipUiTests>
    </properties>

</project>



Scopes

As you can see we used maven scope to limit the transitivity of a dependency.  Below are the scopes we have used in the above pom.xml files.

    test
        - This scope indicates that the dependency is not required for normal use of the application, and is only available for the test compilation and execution phases.
 
    compile
        - This is the default scope, compile dependencies are available in all classpaths of a project. Furthermore, those dependencies are propagated to dependent projects.


Plugin and Configuration management for UI tests

maven-surefire-plugin
 
This plugin can be used during the test phase of the build lifecycle to execute our tests.  You can define many configurational properties such as mentioned below which becomes very handy when organizing your maven test project. 
         eg :
         
                       <skipTests>${skipUiTests}</skipTests>

We use this property tag to skip all Ui tests from regular builds and enable UI tests only  in build servers

                <property>
                        <name>carbon.zip</name>
                        <value>${basedir}/../../distribution/target/wso2bam-${project.version}.zip</value>

</property>

You need to provide the correct location where your product zip file resides. Test suite will extract the zip file found from this place to ${basedir}/target directory and start running your test class.

maven-clean-plugin

This Plugin removes files generated at build-time in a project's directory.Clean Plugin assumes that these files are generated inside the target directory.

maven-dependency-plugin

Basically this plugin is capable of manipulating artifacts. You can define operations like copying artifacts from local or remote repositories to a specified location.

maven-jar-plugin

We use this plugin to build and sign jars. Basically you can define two goals inside this plugin.
jar:jar
                    - creates a jar file for your project classes inclusive resources.
jar:test-jar
                    - creates a jar file for your project test classes.

maven-resources-plugin

Copies project resources to the output director.

Classes

We will now elaborate source codes for each classes categorized under respective UI modules.

tests-common

HomePage.java

Home page class holds the information of product page. Also contains a sign-out method.

package org.wso2.bam.integration.ui.pages.home;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.wso2.bam.integration.ui.pages.UIElementMapper;
import org.wso2.bam.integration.ui.pages.login.LoginPage;

import java.io.IOException;

public class HomePage {

    private static final Log log = LogFactory.getLog(HomePage.class);
    private WebDriver driver;
    private UIElementMapper uiElementMapper;

    public HomePage(WebDriver driver) throws IOException {
        this.driver = driver;
        this.uiElementMapper = UIElementMapper.getInstance();
        // Check that we're on the right page.
        if (!driver.findElement(By.id(uiElementMapper.getElement("home.dashboard.middle.text"))).getText().contains("Home")) {
            throw new IllegalStateException("This is not the home page");
        }
    }

    public LoginPage logout() throws IOException {
        driver.findElement(By.xpath(uiElementMapper.getElement("home.greg.sign.out.xpath"))).click();
        return new LoginPage(driver);
    }
}

LoginPage.java

Basically performs UI Login test case scenario. ie. This class contains methods to login to wso2 products.

package org.wso2.bam.integration.ui.pages.login;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.wso2.bam.integration.ui.pages.UIElementMapper;
import org.wso2.bam.integration.ui.pages.home.HomePage;

import java.io.IOException;

public class LoginPage {
    private static final Log log = LogFactory.getLog(LoginPage.class);
    private WebDriver driver;
    private UIElementMapper uiElementMapper;

    public LoginPage(WebDriver driver) throws IOException {
        this.driver = driver;
        this.uiElementMapper = UIElementMapper.getInstance();
        // Check that we're on the right page.
        if (!(driver.getCurrentUrl().contains("login.jsp"))) {
            // Alternatively, we could navigate to the login page, perhaps logging out first
            throw new IllegalStateException("This is not the login page");
        }
    }

    /**
     * Provide facility to log into the products using user credentials
     *
     * @param userName login user name
     * @param password login password
     * @return reference to Home page
     * @throws java.io.IOException if mapper.properties file not found
     */
    public HomePage loginAs(String userName, String password) throws IOException {
        log.info("Login as " + userName);
        WebElement userNameField = driver.findElement(By.name(uiElementMapper.getElement("login.username")));
        WebElement passwordField = driver.findElement(By.name(uiElementMapper.getElement("login.password")));
        userNameField.sendKeys(userName);
        passwordField.sendKeys(password);
        driver.findElement(By.className(uiElementMapper.getElement("login.sign.in.button"))).click();
        return new HomePage(driver);
    }
}

BAMIntegrationUiBaseTest.java

This is an abstract class which basically helps us to create custom automation context objects and we can define environment related methods those can be used regularly inside our test cases. This class can be extended by other test classes which in-turn eliminates code duplication inside the project.

package org.wso2.bam.integration.ui.pages;

import org.wso2.carbon.automation.engine.context.AutomationContext;
import org.wso2.carbon.automation.engine.context.TestUserMode;
import org.wso2.carbon.automation.test.utils.common.HomePageGenerator;

import javax.xml.xpath.XPathExpressionException;

public abstract class BAMIntegrationUiBaseTest {

    protected AutomationContext automationContext;

    protected void init() throws Exception {
        automationContext = new AutomationContext("BAM", "bam001", TestUserMode.SUPER_TENANT_ADMIN);
    }


    protected String getServiceUrl() throws XPathExpressionException {
        return automationContext.getContextUrls().getServiceUrl();
    }

    protected String getLoginURL() throws XPathExpressionException {
        return HomePageGenerator.getProductHomeURL(automationContext);
    }
}


UIElementMapper.java

The objective of this class is to read mapper.properties file and load it's uiElements into properties object.

package org.wso2.bam.integration.ui.pages;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;


public class UIElementMapper {
    public static final Properties uiProperties = new Properties();
    private static final Log log = LogFactory.getLog(UIElementMapper.class);
    private static UIElementMapper instance;

    private UIElementMapper() {
    }

    public static synchronized UIElementMapper getInstance() throws IOException {
        if (instance == null) {
            setStream();
            instance = new UIElementMapper();
        }
        return instance;
    }

    public static Properties setStream() throws IOException {

      InputStream inputStream = UIElementMapper.class.getResourceAsStream("/mapper.properties");

        if (inputStream.available() > 0) {
            uiProperties.load(inputStream);
            inputStream.close();
            return uiProperties;
        }
        return null;
    }

    public String getElement(String key) {
        if (uiProperties != null) {
            return uiProperties.getProperty(key);
        }
        return null;
    }
}

Mapper.properties

Includes essential configurational properties related to tests. Should be placed inside …../tests-common/ui-pages/src/main/resources directory.  Below is an excerpt of a mapper.properties file.

….
login.username=username
login.password=password
login.sign.in.button=button
home.dashboard.middle.text=middle
….

To view a structure of a complete mapper.properties file click on here.

Writing the test case

Add the following test class (LoginTestCase.java)  to the module tests-ui-integration .

The basic objective of this class is to perform and verify UI login to bam server. Note how we extended  BAMIntegrationUiBaseTest inside our LoginTestCase class. @BeforeClass annotation allows us to execute the testLogin() method before the testLogin() method which is under the @Test annotation. Inside the setUp() (Under @Before class) we call  BAMIntegrationUiBaseTest class  init() method which initialises the automation context and then we can invoke getWebDriver() of BrowserManager class in-order to derive the Webdriver instance. After performing the required UI operation define inside testLogin() method we can quit the driver, closing every associated window as shown under tearDown() method. ( Note that @AfterClass annotation allows to execute  tearDown() after performing the stated operations inside testLogin() method.)


package org.wso2.bam.ui.integration.test;

import org.openqa.selenium.WebDriver;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import org.wso2.carbon.automation.extensions.selenium.BrowserManager;
import org.wso2.bam.integration.ui.pages.BAMIntegrationUiBaseTest;
import org.wso2.bam.integration.ui.pages.home.HomePage;
import org.wso2.bam.integration.ui.pages.login.LoginPage;

public class LoginTestCase extends BAMIntegrationUiBaseTest {

    private WebDriver driver;

    @BeforeClass(alwaysRun = true)
    public void setUp() throws Exception {
        super.init();

        driver = BrowserManager.getWebDriver();
        driver.get(getLoginURL());
    }

    @Test(groups = "wso2.bam", description = "verify login to bam server")
    public void testLogin() throws Exception {
        LoginPage test = new LoginPage(driver);
        HomePage home = test.loginAs(automationContext.getSuperTenant().getTenantAdmin().getUserName(),
                automationContext.getSuperTenant().getTenantAdmin().getPassword());
        home.logout();
        driver.close();
    }

    @AfterClass(alwaysRun = true)
    public void tearDown() throws Exception {
        driver.quit();
    }
}

Note :

Click on BrowserManager to view the source of the Browsermanager class. 

Execution of tests

In-order to run  LoginTestCase follow the steps mentioned below.

Configuring automation.xml

Click automation.xml to learn more on automation.xml file. We will now consider relevant segments you need to draw your attention in-order to execute our UI test scenario with a short description underneath each segment.

   <tools>
        <selenium>
            <!-- Change to enable remote webDriver -->
            <!-- URL of remote webDriver server  -->
            <remoteDriverUrl enable="false">http://10.100.2.51:4444/wd/hub/</remoteDriverUrl>

            <!-- Type of the browser selenium tests are running" -->
            <browser>
                <browserType>firefox</browserType>
                <!-- path to webDriver executable - required only for chrome-->
                <webdriverPath enable="false">/home/test/name/webDriver</webdriverPath>
            </browser>
        </selenium>
    </tools>

Description:

Above configuration will help us to define the browser type the test should run and define web driver path and the choice of enabling / disabling the remote web driver instance.


    <userManagement>
        <superTenant>
            <tenant domain="carbon.super" key="superTenant">
                <admin>
                    <user key="superAdmin">
                        <userName>admin</userName>
                        <password>admin</password>
                    </user>
                </admin>
                <users>
                    <user key="user1">
                        <userName>testuser11</userName>
                        <password>testuser11</password>
                    </user>
                    <user key="user2">
                        <userName>testuser21</userName>
                        <password>testuser21</password>
                    </user>
                </users>
            </tenant>
        </superTenant>
    </userManagement>

Description:

To register a set of system wide users at the test initiation stage.Note the admin super tenant and set of tenant users controlled by the admin super tenant.


 <platform>
        <!--
        cluster instance details to be used to platform test execution
        -->
        <productGroup name="BAM" clusteringEnabled="false" default="true">

            <instance name="bam001" type="standalone" nonBlockingTransportEnabled="false">
                <hosts>
                    <host type="default">localhost</host>
                </hosts>
                <ports>
                    <port type="http">9763</port>
                    <port type="https">9443</port>
                </ports>

                <properties>
                    <!--<property name="webContext">admin</property>-->
                </properties>
            </instance>

        </productGroup>
    </platform>

Description:

You can define different product groups for the product category (In our case we can specify as BAM) together with enable/disable clustering feature (true/ false). Note how these configuration helps us to initialise the automation context inside  BAMIntegrationUiBaseTest class.


Add the following listeners entries to testng.xml file.


<listeners>
        <listener class-name="org.wso2.carbon.automation.engine.testlisteners.TestExecutionListener">
        <listener class-name="org.wso2.carbon.automation.engine.testlisteners.TestManagerListener">
        <listener class-name="org.wso2.carbon.automation.engine.testlisteners.TestReportListener">
        <listener class-name="org.wso2.carbon.automation.engine.testlisteners.TestSuiteListener">
        <listener class-name="org.wso2.carbon.automation.engine.testlisteners.TestTransformerListener">
    </listener></listener></listener></listener></listener></listeners>

 <test name="facebook-connector" preserve-order="true" parallel="false">
        <classes>
            <class name="org.wso2.carbon.connector.integration.test.facebook.FacebookConnectorIntegrationTest">
        </class></classes>
    </test>


2.

 <test name="Login" preserve-order="true" verbose="2">
        <classes>
            <class name="org.wso2.bam.ui.integration.test.LoginTestCase"/>
        </classes>
    </test>

Description:

     1 - Implementing TestNG listener interfaces provide a way to call event handlers inside custom listener classes, thus this makes possible to do pre-defined operations in the TestNG execution cycle.  Click here to learn more on these listener classes.

     2 - Provide the test class you need to execute. This will run the mentioned class only in the test suite.

Note :

Alternatively you can execute a whole test package which contains one or many test classes in the test suite. To do so you can simply add  the below snippet to the testng.xml file.

<test name="Login-tests" preserve-order="true" parallel="false">
        <packages>
            <package name="org.wso2.bam.ui.integration.test"/>        
        </packages>
 </test>



Execute the below maven command.
            mvn install -DskipUiTests=false

Summary

This article provided a step-by-step guide on our UI testing scenario. This article can be used as a foundation and guide for users to add UI modules to products and implement different UI testing scenarios.

Saturday, June 7, 2014

By Dimuthu De Lanerolle
05th June 2014

How to write Facebook connector integration tests using WSO2 Test Automation Framework


WSO2

Abstract


This article will focus on providing initial guidance to software developers to implement connector integration tests with the WSO2 Test Automation Framework. You can use Facebook connector to invoke its operations and to connect with your own Facebook profile. We also illustrate sample code snippets to demonstrate usage of the Facebook connector.




Facebook





Table of contents

Introduction
What are connectors?
About WSO2 ESB connectors
How to write a connector integration test
Creating an event
Uploading the connector zip file
Testing scenario
Running the test class
Resources
Summary

 

Introduction


We assume readers have basic knowledge on the TestNG framework. You can refer to TestNG documentation for the initial knowledge required. To become more familiar with WSO2 Test Automation Framework and to follow generic rules on writing integration tests with WSO2 TAF, refer to WSO2 TAF documentation.

In this article, we analyze the basic scenario of using Facebook connectors to write a sample integration test to get event details of a posted event in a given Facebook profile.

What are connectors?


A connector allows you to interact with a third-party product’s functionality and data from your message flow.

About WSO2 ESB connectors 


WSO2 ESB allows you to create your own connectors or use pre-implemented connectors, which are capable of allowing your message flows to connect and interact with third-party services, such as Facebook,Twitter, Twilio, Google Spreadsheet, etc.

For example, let’s think about a situation where you have enabled Twitter and Google Spreadsheet connectors in your ESB instance; your message flow could receive requests containing a user's Twitter name and password, log into the user's Twitter account, get a list of the user's followers, and write that information to a Google spreadsheet. Each connector provides a set of operations. After adding the required connector to your ESB instance, you can start invoking these operations inside your test class.

Click on this link below to download some pre-implemented connectors.
https://github.com/wso2/esb-connectors/tree/master/distribution

How to write a connector integration test


We will now illustrate some key steps involved in tackling this problem.

To start with, you need to create a module in your test location, e.g. you can start writing your tests in the following location.

…./home/xxx/xxxx/esb-connectors/

For this illustration we will consider a situation where your ESB instance interacts with the Facebook connector.

1. You can clone the WSO2 ESB connector module from the following github HTTP clone URL
https://github.com/wso2-dev/esb-connectors.git

2. Now find “Facebook” module inside esb-connectors

Build the connector and place the generated facebook.zip file in xxxx/esb-connectors/facebook/src/test/resources/artifacts/ESB/connectors

Here are the basic dependencies you need have inside the ....esb-connectors/facebook/pom.xml file.

Note:

You might need to replace the versions of the dependencies listed here in accordance with the WSO2 ESB version you are running (these dependency versions will work with WSO2 ESB 4.8.1 only).

<dependencies>
        <dependency>
            <groupId>org.wso2.carbon.automation</groupId>
            <artifactId>org.wso2.carbon.automation.engine</artifactId>
            <version>${automation.framework.version}</version>
        </dependency>
        <dependency>
            <groupId>org.wso2.carbon.automation</groupId>
            <artifactId>org.wso2.carbon.automation.test.utils</artifactId>
            <version>${automation.framework.version}</version>
        </dependency>
        <dependency>
            <groupId>org.wso2.carbon.automation</groupId>
            <artifactId>org.wso2.carbon.automation.extensions</artifactId>
            <version>${automation.framework.version}</version>
        </dependency>
        <dependency>
            <groupId>org.wso2.carbon</groupId>
            <artifactId>org.wso2.carbon.integration.common.extensions</artifactId>
            <version>${common.version}</version>
        </dependency>
        <dependency>
            <groupId>org.wso2.carbon</groupId>
            <artifactId>org.wso2.carbon.integration.common.admin.client</artifactId>
            <version>${common.version}</version>
        </dependency>
        <dependency>
            <groupId>org.wso2.carbon</groupId>
            <artifactId>org.wso2.carbon.mediation.library.stub</artifactId>
            <version>${stub.version}</version>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>javax.servlet</groupId>
                    <artifactId>servlet-api</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.wso2.carbon</groupId>
            <artifactId>org.wso2.carbon.proxyadmin.stub</artifactId>
            <version>${proxyadmin.stub.version}</version>
        </dependency>
        <dependency>
            <groupId>org.wso2.carbon</groupId>
            <artifactId>org.wso2.carbon.mediation.initializer</artifactId>
            <version>${stub.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.synapse</groupId>
            <artifactId>synapse-core</artifactId>
            <version>${synapse.core.version}</version>
        </dependency>
        <dependency>
            <groupId>org.wso2.carbon</groupId>
            <artifactId>org.wso2.carbon.mediation.configadmin.stub</artifactId>
            <version>${stub.version}</version>
        </dependency>
    </dependencies>

    <properties>
        <automation.framework.version>4.3.1-SNAPSHOT</automation.framework.version>
        <stub.version>4.2.0</stub.version>
        <synapse.core.version>2.1.1-wso2v7</synapse.core.version>
       <common.version>4.3.0-SNAPSHOT</common.version>
       <proxyadmin.stub.version>4.2.1</proxyadmin.stub.version>
    </properties>


Note :

There are several points to ponder when writing connector-related test classes. We will now list down each and you should carefully read the notes below as these will be practically used inside the sample test class we will be writing soon.


1. Your ESB distribution should contain the following entries in its axis2.xml
    You can find the axis2.xml in wso2esb-4.8.1/repository/conf/axis2 inside the distribution.

           <messageFormatter contentType="text/javascript" class="org.wso2.carbon.relay.ExpandingMessageFormatter"/>

            <messageFormatter contentType="text/html" class="org.wso2.carbon.relay.ExpandingMessageFormatter"/>

            <messageBuilder contentType="text/javascript" class="org.wso2.carbon.relay.BinaryRelayBuilder"/>

            <messageBuilder contentType="text/html" class="org.wso2.carbon.relay.BinaryRelayBuilder"/>

            <messageFormatter contentType="application/json" class="org.apache.synapse.commons.json.JsonStreamFormatter"/>

            <messageBuilder contentType="application/json" class="org.apache.synapse.commons.json.JsonStreamBuilder"/>

            <messageFormatter contentType="application/octet-stream" class="org.wso2.carbon.relay.ExpandingMessageFormatter"/>

            <messageBuilder contentType="application/octet-stream" class="org.wso2.carbon.relay.BinaryRelayBuilder"/>


2. For this test class scenario, we will have to create a new module “tests-common” form the esb-connectors module level and create another module and name it “admin-clients” in-order to place product specific admin clients. In our case we will add a few source classes, namely “ProxyAdminClients.java” and “SynapseConfigAdminClient.java” to this package so that every test class inside our “Facebook” connector module can directly invoke methods inside these classes. We will look into more details in this regard at a later stage.

Note: To refer more on ProxyAdminClients.java and SynapseConfigAdminClient.java refer to the links below that contain sample codes for these classes.


[1]  ProxyServiceAdminClient.java

[2]  SynapseConfigAdminClient.java


3. create another module called “integration-test-utils” inside  “tests-common”. In-order to maintain consistency and convenience between test classes inside numerous test modules we will implement a generic base test class that contains all the common methods that in most cases every test class we add to our test module might be using. We will name it “ESBIntegrationConnectorBaseTest.java” class and as mentioned most of the common methods for the whole module will be readily available to other tests classes to extend and carry out their work. For instance our sample test class “FacebookConnectorIntegrationTest .java”
class will extend this “ESBIntegrationConnectorBaseTest.java” class at the first place so that we can waive the burden of such tedious, repetitive workload such as initialization of AutomationContext objects, writing requests, and reading responses. This will save our time in repeating many code snippets every time we add a new test class to our module.


Given below is the structure.

/esb-connectors/
         |--> tests-common
         |             |--> admin-clients
         |                     |--> ProxyServiceAdminClient.java
         |                     |--> SynapseConfigAdminClient.java
         |
         |             |--> integration-test-utils
         |                     |--> ESBIntegrationConnectorBaseTest.java
         |                     |--> ESBTestCaseUtils.java
         |
         |--> facebook/src/test/
                       | java
                               |-->org.wso2.carbon.connector.integration.test.facebook
                                     |-->  FacebookConnectorIntegrationTest.java
                       | resources
                               | artifacts
                                     |-->AXIS2
                                     |-->ESB
                                              |.....
                                              |--> connectors
                                                          |--> facebook.zip
                                              |.....
                              | axis2config
                              | client.modules
                              | keystores
                              | security
                              | automation.xml
                              | automationSchema.xsd
                              | emma.properties
                              | filters.txt
                              | instrumentation.txt
                              | testng.xml

To begin with, as mentioned above, navigate to integration-test-utils package and create a new java class. We will name it ESBIntegrationConnectorBaseTest.java. This is the class we should place methods that are common to almost all test classes.

In most cases, it is inevitable that we create an Automation context object for our test scenarios. Automation context object is more of a custom runtime environment that suits running your tests. WSO2 Test Automation Framework will allow you to create an AutomationContext object in accordance with the provided parameters given at the initial stage of the test.

Implement a protected  init() method and create an instance from the AutomationContext.java class by passing relevant input parameters to the constructor of the AutomationContext class.

new AutomationContext("ESB", TestUserMode.SUPER_TENANT_ADMIN);

Here “ESB” is an already defined productGroup name in the automation.xml file.

To learn more about the automation.xml file and its capabilities refer to the below link that describes the automation.xml in depth.

[1] Automation.xml File Description

Refer to the below link for automation.xml file.

[1] automation.xml

Moreover, note that there are several types of constructors readily available in the AutomationContext.java class enabling you to define the range of automation instances as per your requirement.


In addition, you need to implement a login() method to perform the login operation to the ESB server and obtain a session cookie. Given below is a sample code snippet for a login method and you can create your own using this as a foundation.


  public String login() throws IOException,
            LoginAuthenticationExceptionException, XPathExpressionException,
            XMLStreamException, SAXException, URISyntaxException {
        LoginLogoutClient loginLogoutClient = new LoginLogoutClient(automationContext);
        return loginLogoutClient.login();
    }


Moreover, you can add similar common methods to the  ESBIntegrationConnectorBaseTest.java class that you might need when writing your test scenarios. Note how to derive backend URLs, usernames, and passwords.

Now let’s look into more details relating to the starting of writing our test. Create your own test class inside the “Facebook” module. We will name this class FacebookConnectorIntegrationTest .java . Now, as mentioned in the above, you need to extend ESBIntegrationConnectorBaseTest.java class.
 
public class FacebookConnectorIntegrationTest extends ESBIntegrationConnectorBaseTest
{ ….}

@BeforeClass(alwaysRun = true)
    public void setEnvironment() throws Exception {...}

Since we have set alwaysRun = true this configuration method will run regardless of what group it belongs to.

The init(..) method in setEnvironment(..) will initialize the environment essential to run our tests. This is the place where we create and initialize our AutomationContext object. In addition, we can initialize some service variables and instances at the first place before proceeding with the actual test case scenarios.

Check whether you have connector configuration files under .../facebook/src/test/resources/artifacts/ESB directory.

Make sure the existence of the connector (facebook.zip), facebook.properties configuration file and the facebook.xml proxy file in resources directory.

E.g.
…./esb-connectors/facebook/src/test/resources/artifacts/ESB/connectors/facebook.zip

Skim through the properties mentioned  in facebook.properties file. As our test case will basically focus on adding a proxy to the esb server and get a particular event details from the a facebook account we will need to introduce some property tags to facebook.properties file. The usage of facebook.properties file is to store “Facebook” connector specific configurations enabling us to customize our code.

E.g.

facebook.properties


# proxy folder

proxyDirectoryRelativePath=/../src/test/resources/artifacts/ESB/config/proxies/facebook/


# Folder for of the Rest Request files

requestDirectoryRelativePath=/../../../../../../src/test/resources/artifacts/ESB/config/restRequests/facebook/


# Folder for the resources to be used

resourceDirectoryRelativePath=/../../../../../../src/test/resources/artifacts/ESB/config/resources/facebook/


# Access Token

accessToken=CAACEdEose0cBAERyQe4ow7IJib9SFkVZBPtasVc1yovfeJNTK1N5RPqYcsm2JXELw819E9GfWYiEYlOA350JT3hyZBaDihfLm9IqScGJPEfmKLqfgpph9UBRpmOi2tUXRgAP8E8jHbzQeQctWjYlo1IwSJnCVAqcAsZCzEnM3WTkuIb251GfA06dZB6qGbiKSqZA1EhbnrQZDZD


# Third party user to create invitation and tag photo; must be a friend.

friendId=1236947282


# User profile ID

userId=100007639237322


# The message text of the notification in method PublishNotification

template=This is Application Notification


# Page Access token
pageAccessToken=CAACEdEose0cBAFZBmMiJ7SmgKZBe11TeZBn4ZC5CVFFC2IQDZCPfUf1ZBboDllC2iZCyly1wOu0XHiuGtBUvqO2j1XLur3RkhlnjvMnHtymwrZBgXxoU1pyubSXgSqZAryMvJMaJt0xMf7ZCZB2iJCjEL1xORXPQUwpMWQdKpK4l0VKSnUXhTHV2giQXPxShOobmfkwlEkY8WqJoAZDZD
# The page Id which received 50 likes.
pageId=473300532775365

# General Description to be used
description=Connector Development

# General Message to be used
message=Connector Development Message

#Event ID
eventId=630793950344316

# third party user to be banned/unbanned needs to be added to Application
appUserId=100008133212722

# Application Id
appId=491694797617714

# update page settings (must be a boolean value).
value=false

# Url of the facebook Graph API
apiUrl=https://graph.facebook.com/



Creating an event


Follow these steps for adding an event related properties to facebook.properties file.

1. Create a new Facebook account (or you may use an existing Facebook account known to you for testing purposes)

Note: Your account should be a verified developer account.

Access your Facebook account using your credentials.

2.Obtain an "id" using me/?fields=id in "Graph Explorer" (https://developers.facebook.com/tools/explorer) and copy in to userId in facebook.properties file.

3. Navigate to homepage of your Facebook account and click on “Events”. You should be able to see www.facebook.com/events/list page. Click the Create Event button and now in the “Create New Event” dialog box fill the relevant details and finally click create button. You have successfully added an event to your event list.


You can view the Event ID from the url.
E.g. https://www.facebook.com/events/630793950344316/?ref_dashboard_filter=upcoming

From the above URL, our Event ID would be 630793950344316. Make sure to add another entry to facebook.properties file indicating related details of the event we created.

E.g.
#Created Event ID
eventId=630793950344316
# Name of the event i
eventName=Connector Development Review

Uploading the connector zip file


As mentioned, make sure to place your facebook.zip file in the ../esb-connectors/facebook/src/test/resources/artifacts/ESB/connectors directory.

Refer to the code snippet for ESBIntegrationConnectorBaseTest.java to find the usage of the Facebook connector.

Testing scenario


We will create a proxy service in the ESB server, and with this proxy service, we will call the api-endpoint of the event list from the Facebook account and verify its details.

Running the test class


Add following xml elements to testng.xml file.

 <listeners>
        <listener class-name="org.wso2.carbon.automation.engine.testlisteners.TestExecutionListener"/>
        <listener class-name="org.wso2.carbon.automation.engine.testlisteners.TestManagerListener"/>
        <listener class-name="org.wso2.carbon.automation.engine.testlisteners.TestReportListener"/>
        <listener class-name="org.wso2.carbon.automation.engine.testlisteners.TestSuiteListener"/>
        <listener class-name="org.wso2.carbon.automation.engine.testlisteners.TestTransformerListener"/>
    </listeners>

 <test name="facebook-connector" preserve-order="true" parallel="false">
        <classes>
            <class name="org.wso2.carbon.connector.integration.test.facebook.FacebookConnectorIntegrationTest"/>
        </classes>
    </test>


Resources


facebook.xml file (xx/esb-connectors/facebook/src/test/resources/artifacts/ESB/synapseconfig/facebook)

<?xml version="1.0" encoding="UTF-8"?>
<proxy xmlns="http://ws.apache.org/ns/synapse"
       name="facebook"
       transports="https http"
       startOnLoad="true"
       trace="disable">
   <description/>
   <target>
      <inSequence>
         <property name="apiUrl" expression="json-eval($.apiUrl)"/>
         <property name="accessToken" expression="json-eval($.accessToken)"/>
         <property name="connection" expression="json-eval($.connection)"/>
         <property name="eventId" expression="json-eval($.eventId)"/>
         <property name="fields" expression="json-eval($.fields)"/>
     
         <facebook.init>
            <apiUrl>{$ctx:apiUrl}</apiUrl>
            <accessToken>{$ctx:accessToken}</accessToken>
            <connection>{$ctx:connection}</connection>
            <fields>{$ctx:fields}</fields>
         </facebook.init>
         <switch source="get-property('transport', 'Action')">
            <case regex="urn:getEventDetails">
               <facebook.getEventDetails>
                  <eventId>{$ctx:eventId}</eventId>
               </facebook.getEventDetails>
            </case>
         </switch>
         <respond/>
      </inSequence>
      <outSequence>
         <log/>
         <send/>
      </outSequence>
   </target>
</proxy>
                                                   

FacebookConnectorIntegrationTest.java      

package org.wso2.carbon.connector.integration.test.facebook;

import integrationtestutils.ESBIntegrationConnectorBaseTest;
import org.json.JSONException;
import org.json.JSONObject;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import static org.testng.AssertJUnit.assertEquals;

public class FacebookConnectorIntegrationTest extends ESBIntegrationConnectorBaseTest {

    private Map<String, String> esbRequestHeadersMap = new HashMap<String, String>();

    private Map<String, String> apiRequestHeadersMap = new HashMap<String, String>();

    @BeforeClass(alwaysRun = true)
    public void setEnvironment() throws Exception {

        init("facebook");
        esbRequestHeadersMap.put("Accept-Charset", "UTF-8");
        esbRequestHeadersMap.put("Content-Type", "application/json");

        apiRequestHeadersMap.put("Accept-Charset", "UTF-8");
        apiRequestHeadersMap.put("Content-Type", "application/x-www-form-urlencoded");
    }


    @Test(groups = {"wso2.esb"}, description = "getting facebook event by event ID")
    public void testGetEventDetailsWithMandatoryParameters() throws IOException, JSONException {

        esbRequestHeadersMap.put("Action", "urn:getEventDetails");
        String apiEndPoint =
                connectorProperties.getProperty("apiUrl") + connectorProperties.getProperty("eventId")
                        + "?access_token=" + connectorProperties.getProperty("accessToken");

        RestResponse<JSONObject> esbRestResponse =
                sendJsonRestRequest(proxyUrl, "POST", esbRequestHeadersMap, "esb_getEventDetails_mandatory.txt");

        RestResponse<JSONObject> apiRestResponse = sendJsonRestRequest(apiEndPoint, "GET", apiRequestHeadersMap);

        assertEquals(esbRestResponse.getBody().get("start_time"), apiRestResponse.getBody().get("start_time"));
        assertEquals(esbRestResponse.getBody().get("name"), apiRestResponse.getBody().get("name"));
        assertEquals(esbRestResponse.getBody().get("id"), apiRestResponse.getBody().get("id"));
    }
}



package integrationtestutils;

import org.apache.axiom.om.OMElement;
import org.apache.axis2.context.ConfigurationContext;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.json.JSONException;
import org.json.JSONObject;
import org.wso2.carbon.authenticator.stub.LoginAuthenticationExceptionException;
import org.wso2.carbon.automation.engine.context.AutomationContext;
import org.wso2.carbon.automation.engine.context.TestUserMode;
import org.wso2.carbon.automation.engine.frameworkutils.FrameworkPathUtil;
import org.wso2.carbon.automation.test.utils.axis2client.ConfigurationContextProvider;
import org.wso2.carbon.connector.integration.test.facebook.RestResponse;
import org.wso2.carbon.integration.common.utils.LoginLogoutClient;
import org.wso2.carbon.mediation.library.stub.MediationLibraryAdminServiceStub;
import org.wso2.carbon.mediation.library.stub.upload.MediationLibraryUploaderStub;
import org.wso2.carbon.mediation.library.stub.upload.types.carbon.LibraryFileItem;
import org.xml.sax.SAXException;

import javax.activation.DataHandler;
import javax.xml.stream.XMLStreamException;
import javax.xml.xpath.XPathExpressionException;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.charset.Charset;
import java.rmi.RemoteException;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import static org.wso2.carbon.integration.common.admin.client.utils.AuthenticateStubUtil.authenticateStub;

public class ESBIntegrationConnectorBaseTest {

    private static final Log log = LogFactory.getLog(ESBIntegrationConnectorBaseTest.class);
    private static final float SLEEP_TIMER_PROGRESSION_FACTOR = 0.5f;
    private AutomationContext automationContext;
    private MediationLibraryUploaderStub mediationLibUploadStub;
    private MediationLibraryAdminServiceStub adminServiceStub;
    protected Properties connectorProperties;
    protected String proxyUrl;
    private String repoLocation;
    private String pathToRequestsDirectory;
    protected String pathToResourcesDirectory;


    protected String getBackendURL() throws XPathExpressionException {
        return automationContext.getContextUrls().getBackEndUrl();
    }

    protected String getServiceURL() throws XPathExpressionException {
        return automationContext.getContextUrls().getServiceUrl();
    }

    protected void init(String connectorName) throws Exception {

        automationContext = new AutomationContext("ESB", TestUserMode.SUPER_TENANT_ADMIN);

        ConfigurationContextProvider configurationContextProvider = ConfigurationContextProvider.getInstance();
        ConfigurationContext cc = configurationContextProvider.getConfigurationContext();

        mediationLibUploadStub =
                new MediationLibraryUploaderStub(cc, getBackendURL() + "MediationLibraryUploader");
        authenticateStub("admin", "admin", mediationLibUploadStub);

        adminServiceStub =
                new MediationLibraryAdminServiceStub(cc, automationContext.getContextUrls().getBackEndUrl() + "MediationLibraryAdminService");

        authenticateStub("admin", "admin", adminServiceStub);

        if (System.getProperty("os.name").toLowerCase().contains("windows")) {
            repoLocation = System.getProperty("connector_repo").replace("\\", "/");
        } else {
            repoLocation = System.getProperty("connector_repo").replace("/", "/");
        }

        //new ProxyServiceAdminClient(automationContext.getContextUrls().getBackEndUrl(), login());

        String connectorFileName = connectorName + ".zip";
        uploadConnector(repoLocation, mediationLibUploadStub, connectorFileName);
        byte maxAttempts = 3;
        int sleepTimer = 30000;
        for (byte attemptCount = 0; attemptCount < maxAttempts; attemptCount++) {
            log.info("Sleeping for " + sleepTimer / 1000 + " seconds for connector to upload.");
            Thread.sleep(sleepTimer);
            String[] libraries = adminServiceStub.getAllLibraries();
            if (Arrays.asList(libraries).contains("{org.wso2.carbon.connector}" + connectorName)) {
                break;
            } else {
                log.info("Connector upload incomplete. Waiting...");
                sleepTimer *= SLEEP_TIMER_PROGRESSION_FACTOR;
            }

        }

        adminServiceStub.updateStatus("{org.wso2.carbon.connector}" + connectorName, connectorName,
                "org.wso2.carbon.connector", "enabled");

        connectorProperties = getConnectorConfigProperties(connectorName);

        String pathToProxiesDirectory = repoLocation + connectorProperties.getProperty("proxyDirectoryRelativePath");
        pathToRequestsDirectory = repoLocation + connectorProperties.getProperty("requestDirectoryRelativePath");

        pathToResourcesDirectory = repoLocation + connectorProperties.getProperty("resourceDirectoryRelativePath");

        ESBTestCaseUtils esbTestCaseUtils = new ESBTestCaseUtils();
        OMElement om = esbTestCaseUtils.loadClasspathResource("/home/dimuthu/Desktop/ESB-2/esb-connectors/facebook4/src/test/resources/artifacts/ESB/synapseconfig/facebook/facebook.xml");
        esbTestCaseUtils.updateESBConfiguration(om, getBackendURL(), login());

        proxyUrl = getProxyServiceURL(connectorName);

    }

    protected RestResponse<JSONObject> sendJsonRestRequest(String endPoint, String httpMethod,
                                                           Map<String, String> headersMap) throws IOException, JSONException {

        return this.sendJsonRestRequest(endPoint, httpMethod, headersMap, null, null);
    }

    private Properties getConnectorConfigProperties(String connectorName) {

        String connectorConfigFile;
        try {
            connectorConfigFile =
                    FrameworkPathUtil.getSystemResourceLocation() + File.separator + "artifacts" + File.separator
                            + "ESB" + File.separator + "connector" + File.separator + "config" + File.separator
                            + connectorName + ".properties";
            File connectorPropertyFile = new File(connectorConfigFile);
            InputStream inputStream = null;
            if (connectorPropertyFile.exists()) {
                inputStream = new FileInputStream(connectorPropertyFile);
            }

            if (inputStream != null) {
                Properties prop = new Properties();
                prop.load(inputStream);
                inputStream.close();
                return prop;
            }

        } catch (IOException ignored) {
            log.error("automation.properties file not found, please check your configuration");
        }

        return null;
    }

    private void uploadConnector(String repoLocation, MediationLibraryUploaderStub mediationLibUploadStub,
                                 String strFileName) throws MalformedURLException, RemoteException {

        List<LibraryFileItem> uploadLibraryInfoList = new ArrayList<LibraryFileItem>();
        LibraryFileItem uploadedFileItem = new LibraryFileItem();
        uploadedFileItem.setDataHandler(new DataHandler(new URL("file:" + "///" + repoLocation + "/" + strFileName)));
        uploadedFileItem.setFileName(strFileName);
        uploadedFileItem.setFileType("zip");
        uploadLibraryInfoList.add(uploadedFileItem);
        LibraryFileItem[] uploadServiceTypes = new LibraryFileItem[uploadLibraryInfoList.size()];
        uploadServiceTypes = uploadLibraryInfoList.toArray(uploadServiceTypes);
        mediationLibUploadStub.uploadLibrary(uploadServiceTypes);

    }

    protected String getProxyServiceURL(String proxyServiceName) throws XPathExpressionException {
        return automationContext.getContextUrls().getServiceUrl() + "/" + proxyServiceName;
    }

    protected RestResponse<JSONObject> sendJsonRestRequest(String endPoint, String httpMethod,
                                                           Map<String, String> headersMap, String requestFileName, Map<String, String> parametersMap)
            throws IOException, JSONException {

        HttpURLConnection httpConnection =
                writeRequest(endPoint, httpMethod, RestResponse.JSON_TYPE, headersMap, requestFileName, parametersMap);

        String responseString = readResponse(httpConnection);

        RestResponse<JSONObject> restResponse = new RestResponse<JSONObject>();
        restResponse.setHttpStatusCode(httpConnection.getResponseCode());
        restResponse.setHeadersMap(httpConnection.getHeaderFields());

        if (responseString != null) {
            JSONObject jsonObject = null;
            if (isValidJSON(responseString)) {
                jsonObject = new JSONObject(responseString);
            } else {
                jsonObject = new JSONObject();
                jsonObject.put("output", responseString);
            }

            restResponse.setBody(jsonObject);
        }

        return restResponse;
    }

    private boolean isValidJSON(String json) {

        try {
            new JSONObject(json);
            return true;
        } catch (JSONException ex) {
            return false;
        }
    }

    private HttpURLConnection writeRequest(String endPoint, String httpMethod, byte responseType,
                                           Map<String, String> headersMap, String requestFileName, Map<String, String> parametersMap)
            throws IOException {

        String requestData = "";

        if (requestFileName != null && !requestFileName.isEmpty()) {

            requestData = loadRequestFromFile(requestFileName, parametersMap);

        } else if (responseType == RestResponse.JSON_TYPE) {
            requestData = "{}";
        }

        OutputStream output = null;

        URL url = new URL(endPoint);
        HttpURLConnection httpConnection = (HttpURLConnection) url.openConnection();
        httpConnection.setRequestMethod(httpMethod);

        for (String key : headersMap.keySet()) {
            httpConnection.setRequestProperty(key, headersMap.get(key));
        }

        if (httpMethod.equalsIgnoreCase("POST")) {
            httpConnection.setDoOutput(true);
            try {

                output = httpConnection.getOutputStream();
                output.write(requestData.getBytes(Charset.defaultCharset()));

            } finally {

                if (output != null) {
                    try {
                        output.close();
                    } catch (IOException logOrIgnore) {
                        log.error("Error while closing the connection");
                    }
                }

            }
        }

        return httpConnection;
    }


    private String loadRequestFromFile(String requestFileName, Map<String, String> parametersMap) throws IOException {

        String requestFilePath;
        String requestData;
        requestFilePath = pathToRequestsDirectory + requestFileName;
        requestData = getFileContent(requestFilePath);
        Properties prop = (Properties) connectorProperties.clone();

        if (parametersMap != null) {
            prop.putAll(parametersMap);
        }

        Matcher matcher = Pattern.compile("%s\\(([A-Za-z0-9]*)\\)", Pattern.DOTALL).matcher(requestData);
        while (matcher.find()) {
            String key = matcher.group(1);
            requestData = requestData.replaceAll("%s\\(" + key + "\\)", prop.getProperty(key));
        }
        return requestData;
    }

    private String readResponse(HttpURLConnection con) throws IOException {

        InputStream responseStream = null;
        String responseString = null;

        if (con.getResponseCode() >= 400) {
            responseStream = con.getErrorStream();
        } else {
            responseStream = con.getInputStream();
        }

        if (responseStream != null) {

            StringBuilder stringBuilder = new StringBuilder();
            byte[] bytes = new byte[1024];
            int len;

            while ((len = responseStream.read(bytes)) != -1) {
                stringBuilder.append(new String(bytes, 0, len));
            }

            if (!stringBuilder.toString().trim().isEmpty()) {
                responseString = stringBuilder.toString();
            }

        }

        return responseString;
    }

    private String getFileContent(String path) throws IOException {

        String fileContent = null;
        BufferedInputStream bfist = new BufferedInputStream(new FileInputStream(path));

        try {
            byte[] buf = new byte[bfist.available()];
            bfist.read(buf);
            fileContent = new String(buf);
        } catch (IOException ioe) {
            log.error("Error reading request from file.", ioe);
        } finally {
            if (bfist != null) {
                bfist.close();
            }
        }

        return fileContent;

    }

    protected RestResponse<JSONObject> sendJsonRestRequest(String endPoint, String httpMethod,
                                                           Map<String, String> headersMap, String requestFileName) throws IOException, JSONException {

        return this.sendJsonRestRequest(endPoint, httpMethod, headersMap, requestFileName, null);
    }

    public String login() throws IOException,
            LoginAuthenticationExceptionException, XPathExpressionException,
            XMLStreamException, SAXException, URISyntaxException {
        LoginLogoutClient loginLogoutClient = new LoginLogoutClient(automationContext);
        return loginLogoutClient.login();
    }
}


Summary


This article provided a step-by-step guide on our testing scenario. This article can be used as a foundation and guide for users to implement different testing scenarios using the Facebook connector.