Showing posts with label TestNG. Show all posts
Showing posts with label TestNG. Show all posts

Tuesday, April 15, 2014

Concurrency testing made easy

Having a multi tenant application under test, concurrency testing is the next step of the testing cycle. Testing using concurrent users takes a lot of time and usually ends up in defects not fully reproducible because of timing issues (synchronization) thus making automation of these tests a must.

Automating concurrent tests using selenium / webdriver always brings up one of the most common question in automation testing; Do I need a new window or open a new tab? The correct question should be: how do I open a new session? And here is the tricky part of the tests. If you open a new window or tab you will carry the session from the window you started from. Some sites will suggest, “clearing the cookies” but this involves the whole browser not a specific window thus the newly assigned session id is the same on all existing windows and tabs.

The solution to the above explained problem is to start two or more selenium / webdriver sessions. To simplify these procedures we use a specific Stevia functionality in order to instantiate 2 webdriver controllers. The instantiation is declared in a spring xml file we call test-controllers and includes the following:

<bean id="webDriverControllerSession1"  class="controllers.ControllerSession1" scope="prototype"/>
<bean id="webdriver-s1-driver" class="com.persado.oss.quality.stevia.selenium.core.controllers.registry.Driver"
   p:name="webdriver-s1"
   p:className="controllers.WebDriverS1WebControllerFactoryImpl"/>
<bean id="webDriverControllerSession2"  class="controllers.ControllerSession2" scope="prototype"/>
<bean id="webdriver-s2-driver" class="com.persado.oss.quality.stevia.selenium.core.controllers.registry.Driver"
   p:name="webdriver-s2"
   p:className="controllers.ControllerFactoryImpl"/>


Note: The spring framework schema p should be included in the xml file, and the controller factory implementation should be created in the appropriate packages.

Having the two controllers at hand we can use a Stevia annotation @RunsWithController(controller=controllers.ControllerSession1.class) before any @BeforeClass or @Test annotations.

The next big problem at hand is how to synchronize the sessions in order to have control of the test execution; synchronization in java is quite advanced, there are a lot of techniques, to mention a few: Cyclic barrier and Count Down Latch. These techniques involve heavy coding and it is an overkill to use them. The most appropriate way to synchronize the execution of tests for different controllers is to use the dependency attribute provided by testNG.

Lets use a specific example to put things into perspective:

 Lets assume there is a phonebook application where you can create a custom phone book and assign contacts. You can edit or delete the phonebook as long as it is empty. So lets assume that in session 1 a user created a phonebook and in session 2 a second user assigns a contact. In session 1 the user is not aware that an entry has been added to the phonebook and wants to edit the phonebook name. As soon as the user presses the edit button he / she is presented with an error message stating that you are not allowed to edit on non-empty phonebook.
In the aforementioned scenario there are 2 clear preconditions, as follows:
User in session 1 creates phonebook
User in session 2 adds a contact in phonebook
And a test step with the following action and expected results
User 1 presses the edit button => User is presented with error message

The synchronization sequence is shown below:



For the two preconditions the synchronization could be done using testNG BeforeClass annotations as follows:

@RunsWithController(controllers.ControllerSession1.class)
public void createPreconditionsSession1(String parallelTestsFlag) {
     // User in session 1 creates phonebook
}

@RunsWithController(controllers.ControllerSession2.class)
@BeforeClass(dependsOnMethods = "createPreconditionsSession1")
public void createPreconditionsSession2() throws Exception {
    // User in session 2 adds a contact in phonebook
}

@RunsWithController(controllers.ControllerSession1.class)
@Test(alwaysRun = true, description = "Edit phonebook name")
public void testStep_1() {
    // User presses the edit button => User is presented with error message
}
TestNG will execute createPreconditionsSession1 -> createPreconditionsSession2 -> testStep_1

The next big question that comes to mind with this implementation is how do I run preconditions on different sessions for each step? Lacking a before specific test method and the problem that the usage of a Test annotation is adding noise (step is counted to results as pass even if it is a precondition) we need an extra annotation to execute the precondition. The latest SNAPSHOT of Stevia solves the above problem by introducing an annotation to allow execution of a series of pre and post conditions on a Test annotated step. Lets use the previous example and add a step to our test verifying the story “ User from session 1 created a new phonebook. User in session2 adds a contact. User in session 1 presses the delete button for created phonebook.

@Preconditions(controller=controllers.ControllerSession2.class,value={"test3Pre1", "test3Pre2"}) 
@RunsWithController(controllers. ControllerSession1.class)
@Test(alwaysRun = true, description = "", dependsOnMethods = {"testStep_1"})
public void testStep_2() {
    // User presses the delete button => User is presented with error message
}
public void test3Pre1(){
    // User in session 2 adds a contact on created bucket
}

Note: The second phonebook (delete action test step) is created in the preconditions section along with the first phonebook (edit action test step).

So having these powerful annotations in stevia you can manipulate any test script execution without sacrificing any of the testing standards. As a result the quality is amplified to a new level by having test step pre and post conditions independent of your testing framework (Junit or TestNG).


Read More

Thursday, April 4, 2013

Stevia is coming ...

Back in 2011 while implementing the page object maintainability technique in our test code the idea of creating a framework for testing web apps using java and selenium was created. Couple of years later and after some back and forth the framework became to realization and the name of it is: STEVIA.

Stevia is a Java-based framework created by the encapsulation of three major technologies
·         TestNG (a well-known unit testing framework)
·         Selenium (a well-known framework for automation of user actions via browsers)
·         Spring (the most commonplace user library in Java).

While the release of Stevia is imminent to Github  and in the open source community, a first release of our technical document is uploaded in the following link: http://goo.gl/Is3lA for user reactions gathering. Please have a good read through and/or comment if you think more details are needed.

The document will stay for a week uploaded for everyone who wishes to comment and all constructive reviews are more than welcome. For any technical inquiries and details of the release, feel free to ping as at stevia-release [at] persado.com

Stay tuned for the upcoming release early next week.

UPDATE: We have released it! Check here and on GitHub!
Read More

Friday, March 22, 2013

ReportNG Enrichment With Screenshots

Having a vast number of regression tests it became obvious to our QA team that a good reporting tool for reporting the execution results was of great essence. Our first approach in reporting was to use a simple HTML reporting plug-in for TestNG, known as ReportNG. ReportNG was the obvious way to go just because of its simplicity and easiness in integration with TestNG which is our testing framework. ReportNG provided us with a html report presenting each @Test with a pass / fail state.

Although ReportNG presented an excellent solution to our reporting problems it became apparent that for debugging purposes as well as for defect evidence purposes an extension to include screenshots was needed. These screenshots should be attached to every @Test annotation which fails along with the assertion failure.

In this post I will present you how we implemented the enrichment of ReportNG with screenshots taken by using the Selenium RC api.

ReportNG was used simply by included the dependency tag in the pom.xml of our Project, as:

<dependencies>
   <dependency>
     <groupId>org.uncommons</groupId>
     <artifactId>reportng</artifactId>
     <version>${reportngVersion}</version>
     <scope>test</scope>
 <exclusions>
   <exclusion>
      <groupId>org.testng</groupId>
      <artifactId>testng</artifactId>
   </exclusion>
 </exclusions>
    <dependency>
</dependencies>

Selenium provides a function which capture a screenshot of the current window of the browser. We depend on this and we implement a function in Java which captures the screenshot and places it in the corresponding path in the filesystem as:

public void createScreenshot(ITestResult tr) {
 String testName=parseTestCaseName(tr.getTestClass().getRealClass().getName());
 String imagePath="screenshot-" + testName + "-"
                    + (new SimpleDateFormat("yyyyMMddHHmmss").format(new Date()))                    + ".png";
  try {
   Selenium sel = (Selenium) tr.getAttribute("selenium");
   log.info("Parameter selenium is set to :" + sel);
   if (sel != null) {
    String base64Screenshot = sel.captureEntirePageScreenshotToString("");
    byte[] decodedScreenshot = Base64.decodeBase64(base64Screenshot.getBytes());
    FileOutputStream fos = null;
    try {
     File screenShotFname = new File(currentPath,imagePath);
     fos = new FileOutputStream(screenShotFname);
     fos.write(decodedScreenshot);
     log.info("Stored screenshot in file "+screenShotFname.getAbsolutePath());
     reportLogScreenshot(screenShotFname);
    } finally {
     if (null != fos) {
      fos.close();
     }
     } else {
      log.warn("selenium object is not set, cannot get screenshot");    
      log.warn(new Exception("STACKTRACE"));
     }

    } catch (Exception e) {
     e.printStackTrace();
    }
Ok, now with this function we have the screenshot stored in the corresponding path then we need to draw it into the reportNG log,the implementation is the following:
protected void reportLogScreenshot(File file) {
      System.setProperty("org.uncommons.reportng.escape-output", "false");
  
      String absolute = file.getAbsolutePath();
      int beginIndex = absolute.indexOf(".");
      String relative = absolute.substring(beginIndex).replace(".\\","");
      String screenShot = relative.replace('\\','/');
  
  
Reporter.log("<a href=\"" + screenShot + "\"><p align=\"left\">Error screenshot at " + new Date()+ "</p>");
Reporter.log("<p><img width=\"1024\" src=\"" + file.getAbsoluteFile()  + "\" alt=\"screenshot at " + new Date()+ "\"/></p></a><br />"); 
}
HINT: By default ReportNG escape the special characters, so in order not to see the href link in the reported log instead of your screenshot you will need to disable this escaping as:
System.setProperty("org.uncommons.reportng.escape-output", "false");
Voilà, and now the report log print the capture screenshot and display it as:
ReportNG
For the above implementation to work one should encapsulate it in a TestNG listener which calls these functions when the Test fails. We intent to release the aforementioned implementation along with the listener, as part of an upcoming open source release of our testing framework called Stevia. This framework will provide

  • Selenium and WebDriver API unification: common API methods used for UI Testing, working for both Selenium and WebDriver!, with a flick of a switch 
  • Enhanced verification methods providing element highlighting both in execution and reporting phases
  • Enhanced ReportNG reporting with element highlighting and screenshot inclusion, both for Remote (Selenium Grid) and Local (Selenium RC, WebDriver) methods.
  • Technical documentation, examples, and a lot more. 


Stay tuned for the official release and corresponding download link.... 
Read More