Monday, December 8, 2014

Sample Jaxrs service .....

Here is a sample Jaxrs service which you can use as a backend hosted in Tomcat server.

Package structure ...

  * musicsample
        |- bean
              |- Music
        |- JaxRsApiApplication  
        |- MusicConfig
        |- MusicRestService
        |- MusicService
        |- Server


Server.java

package org.wso2.automation.musicsample;

import org.apache.catalina.Context;
import org.apache.catalina.loader.WebappLoader;
import org.apache.catalina.startup.Tomcat;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.cxf.transport.servlet.CXFServlet;
import org.springframework.web.context.ContextLoaderListener;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;

import java.io.File;
import java.io.IOException;

public class Server {

    private final static Log log = LogFactory.getLog(Server.class);

    public static void main(final String[] args) throws Exception {
        final File base = createBaseDirectory();
        log.info("Using base folder: " + base.getAbsolutePath());

        final Tomcat tomcat = new Tomcat();
        tomcat.setPort(8080);
        tomcat.setBaseDir( base.getAbsolutePath() );

        Context context = tomcat.addContext( "/", base.getAbsolutePath() );
        Tomcat.addServlet( context, "CXFServlet", new CXFServlet() );

        context.addServletMapping( "/rest/*", "CXFServlet" );
        context.addApplicationListener( ContextLoaderListener.class.getName() );
        context.setLoader( new WebappLoader( Thread.currentThread().getContextClassLoader() ) );

        context.addParameter( "contextClass", AnnotationConfigWebApplicationContext.class.getName() );
        context.addParameter( "contextConfigLocation", MusicConfig.class.getName() );

        tomcat.start();
        tomcat.getServer().await();
    }

    private static File createBaseDirectory() throws IOException {
        final File base = File.createTempFile( "tmp-", "", new File("/home/dimuthu/Desktop/JMS"));

        if( !base.delete() ) {
            throw new IOException( "Cannot (re)create base folder: " + base.getAbsolutePath()  );
        }

        if( !base.mkdir() ) {
            throw new IOException( "Cannot create base folder: " + base.getAbsolutePath()  );
        }
        return base;
    }
}


MusicService.java

package org.wso2.automation.musicsample;

import org.springframework.stereotype.Service;
import org.wso2.automation.musicsample.bean.Music;

import java.util.concurrent.ConcurrentHashMap;


@Service
public class MusicService {

    private final ConcurrentHashMap< String, Music> musicCollection = new ConcurrentHashMap< String, Music >();


    public MusicService() {
        init();
    }

    final void init() {
        System.out.println("Welcome To World Of Music .... ");
        Music music = new Music();
        music.setAlbum("Gold");
        music.setSinger("Elton John");
        musicCollection.put(music.getAlbum(), music);
    }

    public Music getByAlbum( final String albumName ) {
      
        return musicCollection.get(albumName);
    }

    public void setMusic( Music music ) {

        musicCollection.put(music.getAlbum(),music);
    }
}








MusicRestService.java

package org.wso2.automation.musicsample;

import org.wso2.automation.musicsample.bean.Music;

import javax.inject.Inject;
import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;

@Path("/music")
public class MusicRestService {

    @Inject
    private MusicService musicService;

    @GET
    @Path("/get")
    @Produces(MediaType.APPLICATION_JSON)
    public Music getMusicInJSON(@QueryParam("album") final String albumName) {
        /*            Music music = new Music();
       music.setAlbum("Beat It !!!");
       music.setSinger("Micheal Jackson");*/

        return musicService.getByAlbum(albumName);
        //return musicService.musicCollection.get("Dimuthu");

    }

    @POST
    @Path("/post")
    @Produces(MediaType.APPLICATION_JSON)
    @Consumes(MediaType.APPLICATION_JSON)
    public Response createMusicInJSONPOST(Music music) {

        musicService.setMusic(music);

        String result = "Album Added : " + music;
        return Response.status(201).entity(result).build();
        //return music.getAlbum();

    }

    @PUT
    @Path("/put")
    @Produces(MediaType.APPLICATION_JSON)
    @Consumes(MediaType.APPLICATION_JSON)
    public Response createMusicInJSONPUT(Music music) {

        musicService.setMusic(music);

        String result = "Album Added : " + music;
        return Response.status(200).entity(result).build();
        //return music;

    }
}

MusicConfig.java

package org.wso2.automation.musicsample;

import org.apache.cxf.bus.spring.SpringBus;
import org.apache.cxf.endpoint.Server;
import org.apache.cxf.jaxrs.JAXRSServerFactoryBean;
import org.codehaus.jackson.jaxrs.JacksonJsonProvider;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.ws.rs.ext.RuntimeDelegate;
import java.util.Arrays;

@Configuration
public class MusicConfig {
    @Bean( destroyMethod = "shutdown" )
    public SpringBus cxf() {
        return new SpringBus();
    }

    @Bean
    public Server jaxRsServer() {
        JAXRSServerFactoryBean factory = RuntimeDelegate.getInstance().createEndpoint( jaxRsApiApplication(), JAXRSServerFactoryBean.class );
        factory.setServiceBeans( Arrays.< Object >asList(musicRestService()) );
        factory.setAddress( "/" + factory.getAddress() );
        factory.setProviders( Arrays.< Object >asList( jsonProvider() ) );
        return factory.create();
    }

    @Bean
    public org.wso2.automation.musicsample.JaxRsApiApplication jaxRsApiApplication() {
        return new JaxRsApiApplication();
    }

    @Bean
    public MusicRestService musicRestService() {
        return new MusicRestService();
    }

    @Bean
    public MusicService musicService() {
        return new MusicService();
    }

    @Bean
    public JacksonJsonProvider jsonProvider() {
        return new JacksonJsonProvider();
    }
}


Music.java

package org.wso2.automation.musicsample.bean;

public class Music {

        String album;
        String singer;

        public String getAlbum() {
            return album;
        }

        public void setAlbum(String album) {
            this.album = album;
        }

        public String getSinger() {
            return singer;
        }

        public void setSinger(String singer) {
            this.singer = singer;
        }

        @Override
        public String toString() {
            return "Your Album " + album + " Of " + singer + " Was Added Successfully";
        }

    }


JaxRsApiApplication.java 

package org.wso2.automation.musicsample;

import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;

@ApplicationPath( "api" )
public class JaxRsApiApplication extends Application {
}




Friday, November 14, 2014

Useful Git commands

Q: How can I merge a distinct pull request to my local git repo ?

A:
   You can easily merge a desired pull request using the following command. If you are doing this merge at first time you need to clone a fresh check-out of the master branch to your local machine and apply this command from the console.
 
git pull https://github.com/wso2-dev/product-esb +refs/pull/78/head

Q: How do we get the current repo location of my local git repo?

A: The below command will give the git repo location your local repo is pointing to.

git remote -v

Q: Can we change my current repo url to a remote repo url

A: Yes. You can point to another repo url as below.

git remote set-url origin https://github.com/dimuthud/carbon-platform-integration-utils.git

Q: What is the git command to clone directly from a non-master branch (eg: two branches master & release-1.9.0 how to clone from release-1.9.0 branch directly without switching to release-1.9.0 after cloning from the master) 

A: Use the following git command.

git clone -b release-1.9.0 https://github.com/wso2/product-apim.git

Maven

Q : I need to go ahead and build no matter i get build failures. Can I do that with maven build?

A: Yes. Try building like this.

mvn clean install -fn 

Q : Can I directly clone a tag of a particular git branch ?

A : Yes. Lets Imagine your tag is 4.3.0 , Following command will let you directly clone the tag instead the branch.

Syntax : git clone --branch <tag_name> <repo_url>

eg:
git clone --branch carbon-platform-integration-utils-4.3.0 https://github.com/wso2/carbon-platform-integration-utils.git



Q : To See git remote urls in more detail

A : git remote show origin



Q: Creating  a new branch

git checkout -b NewBranchName
git push origin master
git checkout master
git branch      (The pointer * represents that, In which branch you are right now.)
git push origin NewBranchName



For More Info : http://stackoverflow.com/questions/9257533/what-is-the-difference-between-origin-and-upstream-on-github

Tuesday, November 11, 2014

Troubleshooting ESB maven dependency issues ....

1. If you are getting the below error when running tests ...

Problem

Exception in thread "HTTP Listener I/O dispatcher-2" java.lang.NoSuchMethodError: org.apache.http.params.HttpProtocolParams.getMalformedInputAction(Lorg/apache/http/params/HttpParams;)Ljava/nio/charset/CodingErrorAction;

Solution

This is due to missing of dependency "HTTPCORE - HttpProtocolParams ". Hence we need to search for maven dependency with contains the method  - getMalformedInputAction(..) . You will easily find that the missing dependency is httpcore version. Therefore you can add the below dependency to your pom.xml.

<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpcore</artifactId>
    <version>4.1</version>
</dependency>


2. If you want to upgrade your activeMQ server version from a lower version (eg: 5.2.0) to an advanced higher version (eg: 5.9.1) you need to add these dependencies to your root pom.xml file.

            <dependency>
                <groupId>org.apache.activemq</groupId>
                <artifactId>activemq-all</artifactId>
                <version>${activemq.version}</version>
                <scope>test</scope>
            </dependency>
            <dependency>
                <groupId>org.apache.activemq</groupId>
                <artifactId>activemq-broker</artifactId>
                <version>${activemq.version}</version>
                <scope>test</scope>
            </dependency>
            <dependency>
                <groupId>org.apache.activemq</groupId>
                <artifactId>activemq-client</artifactId>
                <version>${activemq.version}</version>
                <scope>test</scope>
            </dependency>
            <dependency>
                <groupId>org.fusesource.hawtbuf</groupId>
                <artifactId>hawtbuf</artifactId>
                <version>1.9</version>
                <scope>test</scope>
            </dependency>
            <dependency>
                <groupId>org.apache.geronimo.specs</groupId>
                <artifactId>geronimo-j2ee-management_1.1_spec</artifactId>
                <version>1.0.1</version>
                <scope>test</scope>
            </dependency>
            <dependency>
                <groupId>org.apache.geronimo.specs</groupId>
                <artifactId>geronimo-jms_1.1_spec</artifactId>
                <version>1.1.1</version>
                <scope>test</scope>
            </dependency>
            <dependency>
                <groupId>org.slf4j</groupId>
                <artifactId>slf4j-api</artifactId>
                <version>1.7.5</version>
            </dependency>
            <dependency>
                <!-- any library that uses commons-logging will be directed to slf4j -->
                <groupId>org.slf4j</groupId>
                <artifactId>jcl-over-slf4j</artifactId>
                <version>1.7.5</version>
            </dependency>
            <dependency>
                <!-- any library that uses slf4j will be directed to java.util.logging -->
                <groupId>org.slf4j</groupId>
                <artifactId>slf4j-jdk14</artifactId>
                <version>1.7.5</version>
            </dependency>
            <dependency>
                <groupId>org.slf4j</groupId>
                <artifactId>slf4j-simple</artifactId>
                <version>1.7.5</version>
                <scope>test</scope>
            </dependency>
            <dependency>
                <groupId>org.slf4j</groupId>
                <artifactId>slf4j-log4j12</artifactId>
                <version>1.7.5</version>
            </dependency>


Important code short cuts when writing ESB tests

1. If you need to retrieve the backend service url without the service name you can get the thing done as below.

RestApiAdminClient restApiAdminClient = new RestApiAdminClient(contextUrls.getBackEndUrl(),getSessionCookie();

 2. Replacing and adding a new Proxy service:

private void addProxy() throws Exception {
        String proxy = " <proxy xmlns=\"http://ws.apache.org/ns/synapse\" name=\"StockQuoteProxyPreserveHeaderScenario1\">\n" +
                       "        <target>\n" +
                       "            <inSequence>\n" +
                       "                <property name=\"preserveProcessedHeaders\" value=\"true\"/>\n" +
                       "                <send>\n" +
                       "                    <endpoint>\n" +
                       "                        <address\n" +
                       "                                uri=\"https://localhost:8243/services/UTSecureStockQuoteProxy\"/>\n" +
                       "                    </endpoint>\n" +
                       "                </send>\n" +
                       "            </inSequence>\n" +
                       "            <outSequence>\n" +
                       "                <send/>\n" +
                       "            </outSequence>\n" +
                       "        </target>\n" +
                       "    </proxy>";
        proxy = proxy.replace("https://localhost:8243/services/UTSecureStockQuoteProxy"
                , getProxyServiceURLHttps("UTSecureStockQuoteProxy"));
        addProxyService(AXIOMUtil.stringToOM(proxy));

3. If you need to send a request and do not expect a response :

axisServiceClient.fireAndForget(putRequest, getProxyServiceURLHttp("StockQuoteProxy"), "getQuote")

4. For JMS releated scenarios only :

        OMElement synapse = esbUtils.loadResource("/artifacts/ESB/mediatorconfig/property/ConcurrentConsumers.xml");
        updateESBConfiguration(JMSEndpointManager.setConfigurations(synapse));

5. To update axis2.xml file during a test run.

private ServerConfigurationManager serverManager = new ServerConfigurationManager(context);

        serverManager.applyConfiguration(new File(TestConfigurationProvider.getResourceLocation() + File.separator + "artifacts" + File.separator + "ESB"
                                                  + File.separator + "jms" + File.separator + "transport"
                                                  + File.separator + "axis2config" + File.separator
                                                  + "activemq" + File.separator + "axis2.xml"));



6.Sending API requests :

   HttpResponse response = HttpRequestUtil.sendGetRequest(getApiInvocationURL("stockquote") + "/view/IBM", null);


7. Getting the status code of a response :

 int responseStatus = 0;

        String strXMLFilename = FrameworkPathUtil.getSystemResourceLocation() + "artifacts"
                                + File.separator + "ESB" + File.separator + "mediatorconfig" +
                                File.separator + "property" + File.separator + "GetQuoteRequest.xml";

        File input = new File(strXMLFilename);
        PostMethod post = new PostMethod(getProxyServiceURLHttp("Axis2ProxyService"));
        RequestEntity entity = new FileRequestEntity(input, "text/xml");
        post.setRequestEntity(entity);
        post.setRequestHeader("SOAPAction", "getQuote");

        HttpClient httpclient = new HttpClient();

        try {
            responseStatus = httpclient.executeMethod(post);
        } finally {
            post.releaseConnection();
        }

        assertEquals(responseStatus, 200, "Response status should be 200");

Using filter to route the request to different endpoints based on the HTTP method (eg: HTTP POST & HTTP GET methods)

<inSequence>
                <filter source="get-property('axis2', 'HTTP_METHOD')" regex="POST">
                    <then>
                        <log level="custom">
                            <property name="LOG_METHOD" expression="get-property('axis2', 'HTTP_METHOD')"/>
                        </log>
                        <send>
                            <endpoint>
                                <http method="POST" uri-template="http://localhost:8080/rest/api/music/post"/>
                            </endpoint>
                        </send>
                    </then>
                    <else>
                        <log level="custom">
                            <property name="LOG_METHOD" expression="get-property('axis2', 'HTTP_METHOD')"/>
                        </log>
                        <send>
                            <endpoint>
                                <http method="GET"
                                      uri-template="http://localhost:8080/rest/api/music/get?album=Hotel%20California"/>
                            </endpoint>
                        </send>
                    </else>
                </filter>
            </inSequence>

Monday, November 10, 2014

A Simple HTTP client to retrieve response status code for Wso2 ESB

 int responseStatus = 0;
       
        String strSoapAction = "getQuote";
        // Get file to be posted
        String strXMLFilename = FrameworkPathUtil.getSystemResourceLocation() + "artifacts" + File.separator +
                                "ESB" + File.separator + "mediatorconfig/property/MyRequest.xml";

        File input = new File(strXMLFilename);

        PostMethod post = new PostMethod(getProxyServiceURLHttp("Axis2ProxyService"));
        // Request content will be retrieved directly
        // from the input stream
        RequestEntity entity = new FileRequestEntity(input, "text/xml");
        post.setRequestEntity(entity);
         post.setRequestHeader("SOAPAction", strSoapAction);
        HttpClient httpclient = new HttpClient();

        try {
            int result = httpclient.executeMethod(post);

        } finally {
            post.releaseConnection();
        }

----
MyRequest.xml file

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ser="http://services.samples" xmlns:xsd="http://services.samples/xsd">
   <soapenv:Header/>
   <soapenv:Body>
      <ser:getQuote>
         <!--Optional:-->
         <ser:request>
            <!--Optional:-->
            <xsd:symbol>WSO2</xsd:symbol>
         </ser:request>
      </ser:getQuote>
   </soapenv:Body>
</soapenv:Envelope>


A Simple HTTPClient that you can retrieve both status code and the response payload (Lets imagine your api url is "http://yourip:8280/Transform")

   SimpleHttpClient httpClient = new SimpleHttpClient();
      
    HttpResponse httpResponse = httpClient.doPost("http://yourip:8280/Transform", null, xmlPayload, "application/xml");

    String responsePayload = httpClient.getResponsePayload(httpResponse);

A Simple HTTP-Client to retrieve JSON Array responses correctly 

Notes: Lets imagine you have created an API named "Transform" . In order to retrieve the following JSON array. 

URL url = new URL(getApiInvocationURL("Transform"));
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        conn.setDoOutput(true);
        conn.setRequestMethod("POST");
        conn.setRequestProperty("Content-Type", "application/xml");

        String input = " ----- Your XML Array goes array";

        OutputStream os = conn.getOutputStream();
        os.write(input.getBytes());
        os.flush();

      assertTrue(conn.getResponseCode() == HttpURLConnection.HTTP_OK,
                   "Response Code Mismatch. Expected 200 : Recived " + conn.getResponseCode());

        BufferedReader br = new BufferedReader(new InputStreamReader(
                (conn.getInputStream())));
String response = br.readLine();

        assertTrue(response.contains("{ \"StockQuotes\": { \"Stock\":"), "Response is not in JSON");
        assertTrue(response.contains("IBM"), "Response does not contain Second JSON array element");
        assertTrue(response.contains("WSO2"), "Response does not contain first JSON array element");

Friday, November 7, 2014

TAF Notes

1. automation.xml - without tenants

  <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>
       <tenants>

       </tenants>
    </userManagement>

Thursday, October 16, 2014

How to shift between fault sequence and custom fault sequence


Temporary

<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://ws.apache.org/ns/synapse">
   <proxy name="Faultproxy"
          transports="https http"
          startOnLoad="true"
          trace="disable">
      <description/>
      <target>
         <inSequence>
            <makefault version="soap11">
               <code xmlns:soap11Env="http://schemas.xmlsoap.org/soap/envelope/"
                     value="soap11Env:VersionMismatch"/>
               <reason value="500"/>
               <role/>
            </makefault>
            <respond/>
         </inSequence>
      </target>
   </proxy>
   <proxy name="Axis2ProxyService"
          transports="https http"
          startOnLoad="true"
          trace="disable">
      <description/>
      <target>
         <inSequence onError="fault">
            <property name="FORCE_ERROR_ON_SOAP_FAULT"
                      value="true"
                      scope="default"
                      type="STRING"/>
            <send>
               <endpoint key="Axis2EP"/>
            </send>
         </inSequence>
         <outSequence>
            <log level="full"/>
            <send/>
         </outSequence>
         <faultSequence>
            <log level="custom">
               <property name="faultSequence" value="** Its Inline faultSequence ****"/>
            </log>
            <payloadFactory media-type="xml">
               <format>
                  <sequence xmlns="">$1</sequence>
               </format>
               <args>
                  <arg value="Its Inline faultSequence "/>
               </args>
            </payloadFactory>
            <send/>
         </faultSequence>
      </target>
   </proxy>
   <endpoint name="Axis2EP">
      <address uri="http://localhost:8280/services/Faultproxy"/>
   </endpoint>
   <sequence name="fault">
      <log level="full">
         <property name="MESSAGE" value="Executing default &#34;fault&#34; sequence"/>
         <property name="ERROR_CODE" expression="get-property('ERROR_CODE')"/>
         <property name="ERROR_MESSAGE" expression="get-property('ERROR_MESSAGE')"/>
      </log>
      <log level="custom">
         <property name="fault" value="================Its fault Sequence================"/>
      </log>
      <payloadFactory media-type="xml">
         <format>
            <sequence xmlns="">$1</sequence>
         </format>
         <args>
            <arg value="Its fault Sequence "/>
         </args>
      </payloadFactory>
      <send/>
      <drop/>
   </sequence>
   <sequence name="main">
      <log/>
      <drop/>
   </sequence>
</definitions>

=============================================

Tips To Remember

1. Generating a response inside the proxy service for get request you need to add this property after removing the To header.

<property name="NO_ENTITY_BODY" scope="axis2" action="remove"/>

2. Making of soap-fault generating proxy

<proxy name="Faultproxy"
          transports="https http"
          startOnLoad="true"
          trace="disable">
      <description/>
      <target>
         <inSequence>
            <makefault version="soap11">
               <code xmlns:soap11Env="http://schemas.xmlsoap.org/soap/envelope/"
                     value="soap11Env:VersionMismatch"/>
               <reason value="500"/>
               <role/>
            </makefault>
            <respond/>
         </inSequence>
      </target>
   </proxy>

3. Script mediator to log thread names for wso2 esb each request

<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://ws.apache.org/ns/synapse">
   <localEntry key="stockquoteScript"
               src="file:repository/samples/resources/script/stockquoteTransform.js"/>
   <sequence name="fault">
      <log level="full">
         <property name="MESSAGE" value="Executing default &#34;fault&#34; sequence"/>
         <property name="ERROR_CODE" expression="get-property('ERROR_CODE')"/>
         <property name="ERROR_MESSAGE" expression="get-property('ERROR_MESSAGE')"/>
      </log>
      <drop/>
   </sequence>
   <sequence name="main">
      <in>
         <script language="js" key="stockquoteScript" function="transformRequest"/>
         <send>
            <endpoint>
               <address uri="http://localhost:9000/services/SimpleStockQuoteService"/>
            </endpoint>
         </send>
      </in>
      <out>
         <script language="js" key="stockquoteScript" function="transformResponse"/>
         <log level="custom">
            <property name="Fooo" expression="get-property('threadName')"/>
         </log>
         <send/>
      </out>
   </sequence>
</definitions>


Add a script as a local entry (eg: URL repository/samples/resources/script/stockquoteTransform.js)

* Your stockquoteTransform.js should look like this.

function transformRequest(mc) {
    var symbol = mc.getPayloadXML()..*::Code.toString();
    mc.setPayloadXML(
            <m:getQuote xmlns:m="http://services.samples">
                <m:request>
                    <m:symbol>{symbol}</m:symbol>
                </m:request>
            </m:getQuote>);
}

function transformResponse(mc) {

    mc.setProperty("threadName", java.lang.Thread.currentThread().getName());
    java.lang.Thread.sleep(10000);
}
 
Now open a soapUI and send your soap request to http://localhost:8280/ main sequence.

Now you should be able to see entries displayed in the console of each thread name. Alternatively you can also open the wso2carbon.log file which resides at [CARBON_HOME]/repository/logs to view the thread names generated to handle each request.

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.