Showing posts with label soa. Show all posts
Showing posts with label soa. Show all posts

Saturday, June 22, 2013

WSO2 Test Automation Framework: How to debug product integration tests

WSO2 Test Automation framework use TestNG as its test underlying test engine. And the Surefire Plugin is  used during the "test" phase of the build Lifecycle to execute the integration tests of a product

Debug maven forked Tests

By default, Maven runs your tests in a separate ("forked") process. You can use the maven.surefire.debug property to debug your forked tests remotely, like this:

mvn -Dmaven.surefire.debug 

The tests will automatically pause and await a remote debugger on port 5005. You can then attach to the running tests using your IDE. If you need to configure a different port, you may pass a more detailed value. For example, the command below will use port 8000 instead of port 5005.

mvn -Dmaven.surefire.debug="-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 -Xnoagent -Djava.compiler=NONE" test

Also you can debug tests by adding the following property to surefire plugin configuration section.

-Xms512m -Xmx1024m -XX:MaxPermSize=128m -Xmx1024m -XX:PermSize=256m -XX:MaxPermSize=512m -Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5006 


Debug product components

To debug product components while running integration tests, you have to follow a few steps.

1. Go to your product build location, usually the product distribution can be found at $PRODUCT_NAME/modules/distribution/target. The test framework extract the product distribution at the default build location to test module target directory before running test suites. Then extract the product distribution. And go to the bin directory. 

2. Open wso2server.sh (unix)  and wso2server.bat (in windows). Then edit the script file and put remote debug command generated through your favorite IDE into jvm system properties section.

In my case the jvm remote debug properties generated though my IDE is;

-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5005

Edit the script as follows

$JAVACMD \
    -Xbootclasspath/a:"$CARBON_XBOOTCLASSPATH" \
    -Xms256m -Xmx1024m -XX:MaxPermSize=256m \
    -XX:+HeapDumpOnOutOfMemoryError \
    -XX:HeapDumpPath="$CARBON_HOME/repository/logs/heap-dump.hprof" \
    $JAVA_OPTS \
    -Dcom.sun.management.jmxremote \
    -classpath "$CARBON_CLASSPATH" \
    -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5005
    -Djava.endorsed.dirs="$JAVA_ENDORSED_DIRS" \
    -Djava.io.tmpdir="$CARBON_HOME/tmp" \
    -Dcatalina.base="$CARBON_HOME/lib/tomcat" \
    -Dwso2.server.standalone=true \

3. After editing server startup script, recompress the distribution and replace the product distribution at $PRODUCT_NAME/modules/distribution/target.

Now run the integration tests and execution, you will see that execution will be paused and awaits for remote debugger.

Happy debugging!!







Monday, June 10, 2013

WSO2 Test Automation Framework : Start multiple server instances with different system properties.

WSO2 products servers can be started by providing different system properties.
In order to test certain product features you might need to start the servers with specific server properties. Some of those system properties are mentioned below.

 system-properties:  
   -DhttpPort=           Overrides the HTTP port defined in the config files.  
   -DhttpsPort=          Overrides the HTTPS port defined in the config files.  
   -DosgiConsole=[port]   Start Carbon with Equinox OSGi console.  
   -DportOffset=[port]     Start server with different port offsets.  
   -Dsetup              Can be used to start server after dropping the database  

This post will illustrate how to start WSO2 product servers by providing system properties, and writing integration tests to run on the newly started instances. The test framework start default server instances on default product ports before all test suites.Thus, starting another server on the default product ports on the same host will cause port binding errors. In order to start multiple server instances, you have to set -DportOffset property. If you are going to run multiple product instances on same host for integration tests then setting port offset property is a must.

MultipleServersManager (org.wso2.carbon.automation.core.MultipleServersManager)

The test framework provides MultipleServersManager.java which acts as a container for multiple carbon instances. To use this class, you have to Implement a TestServerManager instance for different types of servers. e.g. AS, ESB etc. Then create a class with appropriate testNG annotation to start server before you suite or test class execution.

TestServerManager (org.wso2.carbon.automation.core.TestServerManager)

TestServerManager is responsible for preparing a Carbon server for test executions, and it shutdown the server after test executions.All test suites/classes which require starting of a new server instance should extend this class.

How to start multiple product instances in integration tests.

Following code sample demonstrate the usage of TestServerManager class. It implement CarbonTestServerManager as the container for TestServerManager instances.


 import java.io.IOException;  
 import java.util.Map;  
   
 import org.wso2.carbon.automation.core.TestServerManager;  
   
 public class CarbonTestServerManager extends TestServerManager {  
   
   public CarbonTestServerManager() {  
   }  
   
   public CarbonTestServerManager(String carbonZip, Map<String, String> startupParameterMap) {  
     super(carbonZip, startupParameterMap);  
   }  
   
   public CarbonTestServerManager(int portOffset) {  
     super(portOffset);  
   }  
   
   public String startServer() throws IOException {  
     String carbonHome = super.startServer();  
     System.setProperty("carbon.home", carbonHome);  
     return carbonHome;  
   }  
   
   public void stopServer() throws Exception {  
     super.stopServer();  
   }  
   
   protected void copyArtifacts(String carbonHome) throws IOException {  
   }  
 }  

Following Test class demonstrates the usage of CarbonServerManager to start two ESB instances with different port offsets. In order to start server instances with different system properties, you need to define a HashMap and put all system properties as key value pairs. Once the map is passed to multiple server manager, the framework will start new server instance with all provided system properties.


 public class NewInstanceTestCase extends CarbonTestServerManager {  
   public MultipleServersManager manager = new MultipleServersManager();  
   public Map<String, String> startupParameterMap1 = new HashMap<String, String>();  
   public Map<String, String> startupParameterMap2 = new HashMap<String, String>();  
   
   @SetEnvironment(executionEnvironments = {ExecutionEnvironment.integration_all})  
   @BeforeClass(groups = {"esb.multi.server"})  
   public void testStartServers() throws IOException {  
     startupParameterMap1.put("-DportOffset", "10");  
     CarbonTestServerManager server1 = new CarbonTestServerManager(System.getProperty("carbon.zip"),  
                                    startupParameterMap1);  
     startupParameterMap2.put("-DportOffset", "20");  
     CarbonTestServerManager server2 = new CarbonTestServerManager(System.getProperty("carbon.zip"),  
                                    startupParameterMap2);  
     manager.startServers(server1, server2);  
   }  
   
   @SetEnvironment(executionEnvironments = {ExecutionEnvironment.integration_all})  
   @Test(groups = {"esb.multi.server"})  
   public void test() {  
     System.out.println("Test server startup with system properties");  
   }  
   
   @SetEnvironment(executionEnvironments = {ExecutionEnvironment.integration_all})  
   @AfterClass  
   public void clean() throws Exception {  
     manager.stopAllServers();  
   }  
 }  

Also note that since test case is depending on carbon zip file to start server instances, you have to use custom annotation @SetEnvironment to skip the test from platform setup. In the above example, test case will be executed only on integration level.

You can find the test case source at here












Thursday, April 18, 2013

How to write your first Platform Automated Test


Objective of this guide on platform test is to enable users to start with automating platform test scenarios using WSO2 Test Automation Framework. This will be a generic guide written based on ESB and Data services servers. We expect readers of this document to have basic knowledge of TestNG framework. You can refer to TestNG documentation to get initial knowledge required.

What it mean by platform

WSO2 product platform compromise of a set of products, which are integrated to support enterprise use cases. For an example governance platform compromise of Governance registry, CEP, Business Activity Monitor and Identity server which provides SOA governance, social enterprise, analytics, lifecycle management capabilities. In the point of automation we call a specific set up a platform, if one or more products are integrated to implement business use case.

Platform Tests

Automation test framework has been designed to conduct testing of platform scenarios
on a complete, integrated system to evaluate the system's compliance with its desired
requirements.Test scenarios will be mainly based on possible applications which can be
developed using WSO2 product platform.

Scenario to be Automated

SOAP service named student data service which will be deployed on WSO2 Data Service Server which is again fronted by ESB proxy to provide RESTful API to manage student resource. The test case will deploy data service on DSS and synapse artifacts on ESB then it invokes the RESTful API exposed via ESB and perform basic GET, POST, DELETE, PUT methods on student resource.

WSO2 library article written by Amila Suriarachchi describe the end to end scenario that is going to automated here.


Where to Add Platform Tests

You checkout and use WSO2 Platform Automated Test Suite to add the test case.


org.wso2.carbon.automation.platform.test.scenarios module can be used to implement platform automated tests.

Download Test Artifacts
Download artifacts required for the scenario from http://wso2.org/files/sample_10.zip you can find the artifact download link in above mentioned OT article also.

Configure Test Environment
  • Navigate to org.wso2.carbon.automation.platform.test.scenarios module and start by adding a new test package.
e.g.
platform-integration/platform-automated-test-suite/1.1.2/org.wso2.carbon.automation.platform.test.scenarios/src/test/java/org.wso2.carbon.automation.platform.scenarios/esb

  • Then go to automation.properties file under src/test/resources and edit following properties.
  1. product.list=DSS,ESB
  2. execution.environment=platform
  3. execution.mode=user
  4. dss.host.name=localhost
  5. dss.http.port=9764
  6. dss.https.port=9444
  7. esb.host.name=localhost
  8. esb.http.port=9763
  9. esb.https.port=9443
  10. esb.nhttp.port=8280
  11. esb.nhttps.port=8243
  12. database.driver.name=com.mysql.jdbc.Driver
  13. jdbc.url=jdbc:mysql://localhost:3306
  14. db.user=root
  15. db.password=root
  16. db.name=STUDENT_DB


You can set ports and host names of DSS and ESB servers according to your server settings.

Framework uses mysql as the default DB for data services. So you need to set connection settings as illustrated in #13 to #16.

  • Import your keystores in to wso2carbon.jks at org.wso2.carbon.automation.platform.test.scenarios/src/test/resources/keystores       You don’t need to do this step if you are using default keystores in WSO2 products.

  • Update admin credentials in userList.csv and tenantList.csv files at src/test/resources with your admin credentials. You don’t need to update other user details. Test framework will automatically populates user list to products. It identify the server list by reading product.list property defined in automation.properties file. These users will be used in various test cases.  Note that all users have permission to read, write, delete and authorize registry space. in terms of other access privileges they are approximately equal to admins. However don’t have super admin privileges.

  • Now navigate to endpintlookup.xml at src/test/resources and put following entry
    "http://localhost:9765/services/StudentService/"


Here we define address endpoint of student data service in endpointlookup.xml. Endpoint lookup is used to replace the endpoints defined in synapse configuration based on the test execution environment. You need to invoke the endpoint replacement functions before updating synapse configuration via test.


Copy Test Artifacts to Framework Artifact Repo

  • Copy StudentService.dbs into src/test/resources/artifacts/DSS/dbs/rdbms/MySql and student.sql into src/test/resources/artifacts/DSS/sql/MySql




Start Writing Your First Platform Test

please refer to methods comments for further clarifications.

 /**  
  * Implement the scenario discussed in  
  * http://wso2.org/library/articles/2012/10/implementing-restful-services-wso2-esb  
  */  
 public class RestFullApiTestCase {  
   private static final Log log = LogFactory.getLog(RestFullApiTestCase.class);  
   private DataServiceBaseTest dataServiceBaseTest;  
   private ESBBaseTest esbBaseTest;  
   private URL url;  
   /**  
    * Initialize test environment by creating instances of DataServiceBaseTest and ESBBaseTest classes, these classes  
    * provide required utility functions such as upload data service, update and revert synapse configuration etc..  
    * <p/>  
    * And resource URI is constructed by getting service URL of esb instance in esbBaseTest.  
    *  
    * @throws Exception - if initialization fails  
    */  
   @BeforeClass(groups = "wso2.esb", alwaysRun = true, description = "initialize test environment")  
   public void testInitialize() throws Exception {  
     esbBaseTest = new ESBBaseTest();  
     dataServiceBaseTest = new DataServiceBaseTest();  
     url = new URL(esbBaseTest.esbServer.getServiceUrl().  
         substring(0, esbBaseTest.esbServer.getServiceUrl().lastIndexOf("/")) + "/students/003");  
   }  
   /**  
    * Execute student.sql on mysql instance which specified in automation.properties file. Then deploy the data service  
    * located at DSS artifact repository.  
    *  
    * @throws Exception - if data service deployment fails  
    */  
   @Test(groups = "wso2.esb", description = "deploy student data service")  
   public void testDeployStudentService() throws Exception {  
     String serviceName = "StudentService";  
     List<File> sqlFileLis = new ArrayList<File>();  
     sqlFileLis.add(dataServiceBaseTest.selectSqlFile("student.sql"));  
     dataServiceBaseTest.deployService(  
         serviceName, dataServiceBaseTest.  
         createArtifact(ProductConstant.SYSTEM_TEST_RESOURCE_LOCATION + "artifacts"  
                 + File.separator + "DSS" + File.separator + "dbs" + File.separator  
                 + "rdbms" + File.separator + "MySql" + File.separator  
                 + "StudentService.dbs", sqlFileLis));  
     log.info(serviceName + " uploaded");  
   }  
 /**  
    * updateESBConfiguration(OMElement synapseConfigOM)  
    * will deployed the artifact defined in the synapse configuration (proxy, endpoints, sequence, message store, etc..)  
    * using admin services API. Framework will go through the configuration and deployed each synapse artifact separately.  
    * If synapse artifact with same name exists in the system, it is deleted delete existing one and redeploy new one.  
    *  
    * @throws Exception - if update of synapse configuration fails.  
    */  
   @Test(groups = "wso2.esb", description = "update synapse config", dependsOnMethods = "testDeployStudentService")  
   public void testUpdateSynapseConfig() throws Exception {  
     String synapseConfigPath = ProductConstant.SYSTEM_TEST_RESOURCE_LOCATION + File.separator +  
                   "artifacts" + File.separator + "ESB" + File.separator + "synapseconfig" +  
                   File.separator + "config67" + File.separator + "synapse.xml";  
     EsbEndpointSetter esbEndpointSetter = new EsbEndpointSetter();  
     OMElement synapseConfigOM =  
         esbEndpointSetter.setEndpointURL(new DataHandler(new URL("file://" + synapseConfigPath)));  
     esbBaseTest.updateESBConfiguration(synapseConfigOM);  
   }  
   /**  
    * Add new student by sending POST request to student resource.  
    *  
    * @throws Exception - if POST request fails.  
    */  
   @Test(groups = "wso2.esb", dependsOnMethods = "testUpdateSynapseConfig", description = "Add new student")  
   public void testAddNewStudent() throws Exception {  
     String addPayload = "<p:Student xmlns:p=\"http://ws.wso2.org/dataservice\">\n" +  
               "   <p:name>tharindu</p:name>\n" +  
               "   <p:email>tharindu@gmail.com</p:email>\n" +  
               "   <p:age>16</p:age>\n" +  
               "   <p:class>8B</p:class>\n" +  
               "   <p:average>83.45</p:average>\n" +  
               "</p:Student>";  
     Reader data = new StringReader(addPayload);  
     Writer writer = new StringWriter();  
     HttpURLConnectionClient.sendPostRequest(data, url, writer, "application/xml");  
   }  
   /**  
    * Check whether the newly added student is exits by sending GET request.  
    *  
    * @throws Exception - if GET request fails.  
    */  
   @Test(groups = "wso2.esb", dependsOnMethods = "testAddNewStudent", description = "get newly added student")  
   public void testGetStudent() throws Exception {  
     HttpResponse response = HttpURLConnectionClient.sendGetRequest(url.toString(), null);  
     assertTrue(response.getData().contains("<Student xmlns=\"http://ws.wso2.org/dataservice\">" +  
                         "<RegistrationNumber>003</RegistrationNumber>" +  
                         "<Name>tharindu</Name><Email>tharindu@gmail.com</Email>" +  
                         "<Age>16</Age><Class>8B</Class><Average>83.45</Average>" +  
                         "</Student>"), "new student has not been added");  
   }  
   /**  
    * Update the student by sending PUT request to student resource. Then verify the whether the student is updatd by  
    * sending GET request to student resource.  
    *  
    * @throws Exception - if PUT request fails.  
    */  
   @Test(groups = "wso2.esb", dependsOnMethods = "testGetStudent", description = "update student")  
   public void testUpdateStudent() throws Exception {  
     String updatePayload = "<p:Student xmlns:p=\"http://ws.wso2.org/dataservice\">\n" +  
                 "   <p:name>amila</p:name>\n" +  
                 "   <p:email>amila@wso2.com</p:email>\n" +  
                 "   <p:age>16</p:age>\n" +  
                 "   <p:class>8A</p:class>\n" +  
                 "   <p:average>67.89</p:average>\n" +  
                 "</p:Student>";  
     Reader data = new StringReader(updatePayload);  
     Writer writer = new StringWriter();  
     HttpURLConnectionClient.sendPutRequest(data, url, writer, "application/xml");  
     System.out.println(writer.toString());  
     HttpResponse response = HttpURLConnectionClient.sendGetRequest(url.toString(), null);  
     assertTrue(response.getData().contains("<Student xmlns=\"http://ws.wso2.org/dataservice\">" +  
                         "<RegistrationNumber>003</RegistrationNumber>" +  
                         "<Name>amila</Name><Email>amila@wso2.com</Email>" +  
                         "<Age>16</Age><Class>8A</Class><Average>67.89</Average>" +  
                         "</Student>"), "new student has not been updated");  
   }  
   /**  
    * Delete the student by sending DELETE request to student resource. Then verify the whether the student is deleted by  
    * sending GET request to student resource.  
    *  
    * @throws Exception - if DELETE request fails.  
    */  
   @Test(groups = "wso2.esb", dependsOnMethods = "testUpdateStudent",  
      description = "delete student and try to get the student again")  
   public void testDeleteStudent() throws Exception {  
     HttpURLConnectionClient.sendDeleteRequest(url, null);  
     HttpResponse response = HttpURLConnectionClient.sendGetRequest(url.toString(), null);  
     assertTrue(response.getData().contains("<Students xmlns=\"http://ws.wso2.org/dataservice\"/>"),  
           "new student has not been deleted");  
   }  
   /**  
    * Undeploy data service after the executing all test methods.   
    * cleanup() will remove all deployed synapse artifacts.  
    *   
    * @throws Exception  
    */  
   @AfterClass(alwaysRun = true)  
   public void deleteService() throws Exception {  
     dataServiceBaseTest.deleteService("StudentService");  
     esbBaseTest.cleanup();  
   }  
 }  


To run the test class you have to add your test classes in testng.xml which is at src/test/resources

 <suite name="PlatformTestSuite" parallel="false">  
   <listeners>  
     <listener class-name="org.wso2.carbon.automation.core.PlatformExecutionManager"/>  
     <listener class-name="org.wso2.carbon.automation.core.PlatformSuiteManager"/>  
     <listener class-name="org.wso2.carbon.automation.core.PlatformAnnotationTransferManager"/>  
     <listener class-name="org.wso2.carbon.automation.core.PlatformTestManager"/>  
     <listener class-name="org.wso2.carbon.automation.core.PlatformReportManager"/>  
   </listeners>  
   <test name="Platform-test-scenarios" preserve-order="true" verbose="2">  
     <classes>  
       <class name="org.wso2.carbon.automation.platform.scenarios.esb.RestFullApiTestCase"/>  
     </classes>  
   </test>  
 </suite>  


How to Execute the Test

Note that all testNG listeners implemented in automation framework need to be registered in testng.xml

You can execute the test inside the module itself using maven surefire plugin or use platform automated test suite (wso2pats) to get the test executed on product setup.

To execute test using maven

  1. Start ESB and DSS servers.
  2. Make sure mysql server is running.
  3. The run the below command
    1. mvn install -DskipTests=false

You can find the surefire reports at target/surefire-reports directory.

To execute test using platform automated test suite

  1. Build the test by skipping tests.
    1. mvn install -DskipTests=true
  2. Then go to /platform-automated-test-suite/version/distribution directory.

  1. Now build the distribution - mvn clean install
  2. Go to distribution/target directory.
  3. You will find the wso2pats-1.1.x.zip distribution.
  4. Extract it and follow the instructions available at INSTALL.txt file.

Running Test on Different Environments
Once you write a test case you can run that test case in different environments. Just need to change the automation properties which can be found src/test/resources directory.


Executing test on integration Environment as a tenant
By default test runs on the product platform environment on user mode. By changing following properties in automation.properties file, users can execute the same test in product setup on tenant mode.

stratos.test=true  
#execution.environment = integration|platform|stratos  
execution.environment=stratos  
#execution.mode = user|tenant|all  
execution.mode=tenant  
port.enable=true  
carbon.web.context.enable=false  
builder.enable=false

Executing as a tenant
if you want to run the test as a tenant. setting below will execute the test as tenant.
execution.mode=tenant  

Executing test on cloud platform( Stratos)
if you want to run your test class against cloud platform,  change setting as below.

stratos.test=true  
execution.environment=stratos  
execution.mode=tenant  

#Stratos server details  
esb.service.host.name=esb.stratoslive.wso2.com
dss.service.host.name=data.stratoslive.wso2.com