Monday, October 18, 2010

Asking PageFactory.initElements() for Help

Some people asked me what kind of things are done by PageFactory.initElements()?
Let's see a simple example to see the difference,PS: code just for Demo, not for formal testing:-):

CommonPage.class(PageObject)
 public class CommonPage {  
      private WebDriver driver;  
      @FindBy(how = How.NAME, using = "q")  
   private WebElement searchtext;  
      @FindBy(how = How.NAME, using = "btnG")  
   private WebElement searchbutton;  
      public CommonPage(WebDriver driver) {  
             this.driver = driver;  
             System.out.println("cool stuff!");  
      }  
      public CommonPage getpages(){  
           driver.get("http://www.google.com/");  
           searchtext.sendKeys("hello, common");  
           return this;  
      }  
      public void searchaction(){  
           searchbutton.click();  
      }  
 }  

Version1, using PageFactory.initElements(WebDriver, Class):
Testcode:
 public class PageFactoryTest {  
      /**  
       * @param args  
       */  
      public static void main(String[] args) {  
           // TODO Auto-generated method stub  
                WebDriver driver = new FirefoxDriver();  
                CommonPage searchpage = PageFactory.initElements(driver, CommonPage.class); 
                searchpage.getpages().searchaction();   
                driver.quit();  
           }  
      }  

Version2, just using New:
Testcode:
 public class PageFactoryTest {  
      /**  
       * @param args  
       */  
      public static void main(String[] args) {  
           // TODO Auto-generated method stub  
                WebDriver driver = new FirefoxDriver();  
                CommonPage searchpage = new CommonPage(driver);  
                searchpage.getpages().searchaction();  
                driver.quit();  
           }  
      }  

So both PageFactory.initElements(driver, CommonPage.class); and new CommonPage(driver); are calling the construct method in order to pass the Driver to Commonpage class.

The difference between these 2 versions is to initialize looking up the WebElement (Fields) you declared in Commonpage.
so if you define your WebElements using @FindBy annotation instead of defining it within your each method directly(eg. driver.findElement(By)), then you need to using PageFactory.initElements() for help.

Tuesday, October 12, 2010

Windows OS performance measurements by 'typeperf' in Ruby

I am writing a Windows resource monitoring script with ‘typeperf’ command in Ruby, so that i can centralize to get all my testing server's OS Health status during performance testing.
I simply get measurements like CPU%, Mem utlization, Disk I/O throughput and Network I/O throughput from Perfmon Counters.

perf_mon_util.rb Class Source code:
 class PerfMonUtil  
  @hostIPs = [];  
  def initialize(hostarray)  
   hostarray = hostarray.sub(/\s/, '');  
   @hostIPs = hostarray.split(',');  
   puts "monitor IP(s): " + @hostIPs * ",";  
  end  
  # the unit of duration is set to minutes by default  
  def perfmon(duration = 5,intv = 1, filepath = "C:\\temp_mon\\")  
   puts @hostIPs;  
   puts intv;  
   sampler_count = (duration.to_i * 60) / intv.to_i;  
   @hostIPs.each { |inputIP|  
    machineIP = inputIP.strip;  
    filename = filepath + machineIP.gsub(/\./,'_') + "-" + Time.now.strftime("%Y%m%d-%H%M%S");  
    Thread.new {  
        system("typeperf",  
        "\\processor(_total)\\% processor time", #CPU%  
        "\\processor(_total)\\% user time",  
        "\\processor(_total)\\% privileged time",  
        "\\Memory\\Available Kbytes", #MEM_Utilization  
        "\\physicaldisk(_total)\\% disk read time", #DISK I/O  
        "\\physicaldisk(_total)\\% disk write time",  
        "\\network interface(*#1)\\bytes total/sec", #NetWork I/O  
        "-s", "#{machineIP}","-si","#{intv}", "-sc", "#{sampler_count}", "-o", filename)  
        Process.exit!(1);  
     }  
   }  
  end  
 end  

Perf_mon_runner.rb source code:
 require 'perf_mon_util'  
 case ARGV.length  
 when 0  
  puts "Please assign monitored machines' name, you can access help by type Perf_mon_runner.rb -help"  
  puts "Process exiting..."  
  Process.exit!(1);  
 when 1  
  if ARGV[0] == "-help"  
   puts "usage samples listed: Perf_mon_runner.rb MonitredIPs Duration Interval FilePath"  
   puts "Perf_mon_runner.rb \"ip1,ip2,ip3\" 1 20 \"D:\\\\temp\\\\\""  
   puts "Monitoring object list(Measurements including):"  
   puts " \\processor(_total)\\% processor time"  
   puts " \\processor(_total)\\% user time"  
   puts " \\processor(_total)\\% privileged time"  
   puts " \\Memory\\Available Kbytes"  
   puts " \\physicaldisk(_total)\\% disk read time"  
   puts " \\physicaldisk(_total)\\% disk write time"  
   puts " \\network interface(*#1)\\bytes total/sec"  
   Process.exit!(1);  
  end  
  puts "put your monitoring files in C:\\temp_mon\\"  
  CPU_Utilization= PerfMonUtil.new(ARGV[0]);  
  CPU_Utilization.perfmon();  
 when 2  
  puts "put your monitoring files in C:\\temp_mon\\"  
  CPU_Utilization= PerfMonUtil.new(ARGV[0]);  
  CPU_Utilization.perfmon(ARGV[1]);  
 when 3  
  puts "put your monitoring files in C:\\temp_mon\\"  
  CPU_Utilization= PerfMonUtil.new(ARGV[0]);  
  CPU_Utilization.perfmon(ARGV[1],ARGV[2]);  
 when 4  
  puts "put your monitoring files in " + ARGV[3]  
  CPU_Utilization= PerfMonUtil.new(ARGV[0]);  
  CPU_Utilization.perfmon(ARGV[1],ARGV[2],ARGV[3]);  
 end  

How to use it in command-line:
Perf_mon_runner.rb ["YourIP1,YourIP2"] [Monitor_Duration] [Sample_Interval] [FilePath]

Real Usage sample:
Perf_mon_runner.rb "YourIP1,YourIP2" 1 20 "D:\\temp\\"

PS: The Monitoring result will write in the file path as .CSV format by default.

i am using Gruff to draw the graph of monitoring result:

the code is pretty straightforward:
 require 'csv'  
 require 'gruff'  
 def parserperfmon(csvfile,targetfile)  
  arraycpu = Array.new;  
  i = 0;  
  CSV.foreach(csvfile) do |rec|  
    # CPU% data is put into arraycpu
    arraycpu[i] = rec[1]  
    i = i+1;  
  end  
  #column title is removed
  arraycpu.shift  
  #String need to be converted to float type in arraycpu
  arraycpu.collect! { |item|  
   item.to_f  
  }  
  g = Gruff::Line.new  
  g.title = "CPU% Monitoring Graph";  
  g.data("CPU%", arraycpu)  
  g.write(targetfile)
 end  

 filename = "D:\\TEMP\\10_201_10_173-20101125-153102\.csv"  
 targetname ="D:\\TEMP\\perf_mon.png"
 parserperfmon(filename,targetname);  

will consider to add : \\System\\Processor Queue Length to monitor the Queue length:
An instantaneous count of threads that are in the processor queue.

Saturday, October 09, 2010

A Hangzhou Trip















A bird's eye-view of West LakeOn the top of the hill













I like the smile face in the middle :-) The question is who did that...













 "Taking look at my tail,  i am changing the color"






















































Friday, October 08, 2010

Raft Flow Chart

I Draw my Raft automation framework flow chart using "TheBrain" Application, here it is:

http://webbrain.com/u/11J0

Sunday, September 26, 2010

Create WaterFall Chart for Performance (Duration)

usually, I wanna know how much load created at certain point of time on my async system, and see how much impact the concurrent load bring to it. So I made a simple Create WaterFall Chart to help me to understand these more clearly.
This is written in Ruby,sorry java guys, it is faster for me to write ruby code than java, but i like java gradually :-)

I am using Google Chart API, Googlecharts, TinyURL services, Thanks you for providing these!

A quick snapshot for waterfall chart(click to see big):




Test code:

 require 'gchart'  
 require 'time'  
 require 'open-uri'  
 require 'uri'  
 require 'accessDB'  
 #Get start and end time stamp from DB  
 startArray = ["2010-09-26 16:58:37.693","2010-09-26 16:58:38.223","2010-09-26 16:58:38.833",  
 "2010-09-26 16:58:39.600","2010-09-26 16:58:40.210","2010-09-26 16:58:40.833","2010-09-26 16:58:55.740"];  
 endArray = ["2010-09-26 16:59:56.543","2010-09-26 16:59:58.183","2010-09-26 17:00:04.217",  
 "2010-09-26 17:00:09.370","2010-09-26 17:00:10.217","2010-09-26 17:00:19.323","2010-09-26 17:00:25.027"];  
 Gchart_string = prepareData(startArray, endArray )  
 Gchart_url = waterFallUrl(Gchart_string)  
 puts minifyUrl(Gchart_url)  

Prepare Google chart Data and para:
 def prepareData(starttime, endtime )  
  if starttime.length > 400  
   puts "\nExceed the maxium number(400) of records."  
   puts "You can try to split them to display.Press Enter to exit..."; $stdout.flush  
   gets  
   break;  
  end  
  # convert Time Object string to Time object Int  
  starttime.collect! { |time1|  
  Time.parse("#{time1}").to_i;  
  }  
  starttime.sort!  
  endtime.collect! { |time2|  
   Time.parse("#{time2}").to_i;  
  }  
  endtime.sort!  
  # prepare data in array, convert to string used by Gchart  
  flag = starttime[0]  
  starttime.collect! { |time1|  
   time1-flag;  
  }  
  endtime.collect! { |time2|  
   time2-flag;  
  }  
  if endtime.last > 3600  
   puts "\nWarning: Duration is too long to display well for Chart(3600s is recommended).";  
   puts "You can split the data to display."  
  end  
  line_xy_string = "";  
  scale_num = 1;  
  cata = 10;  
  while endtime.last > cata  
   cata = cata + 10 ;  
  end  
  scale_num = cata/starttime.length  
  starttime.length.times { |i|   
   line_xy_string << starttime[i].to_s;  
   line_xy_string << ",";  
   line_xy_string << endtime[i].to_s;  
   line_xy_string << "|";  
   line_xy_string << ((i+1)*scale_num).to_s + "," + ((i+1)*scale_num).to_s;  
   # ignore the last "|"  
   if ((i+1) < starttime.length)  
    line_xy_string << "|";  
   end  
  }  
  line_xy_string << "&chds=";  
  line_xy_string << "0,#{cata}";  
  line_xy_string << "&chxt=x,x,y"  
  line_xy_string << "&chxr=0,0,#{cata}"  
  line_xy_string << "&chxl=1:|Duration(sec)"  
  return line_xy_string;  
 end  


Get Google Chart URL:
 def waterFallUrl(chart_string)  
  output_url = Gchart.line_xy(:size => '700x400',  
       :title => "WaterFall Charts",  
       :custom => "chd=t:#{chart_string}")  
  #"&chxr=0,0" is automatic added to the end of string,  
  #remove it due to already identify one in chart_string  
  output_url.chomp!("&chxr=0,0");  
  return output_url  
 end  

Minify URL using tinyURL:
 def minifyUrl(gchartUrl)  
  # convert URL to escaped one  
  escaped_url = URI.escape(gchartUrl, Regexp.new("[^#{URI::PATTERN::UNRESERVED}]"))  
  short_url = open("http://tinyurl.com/api-create.php?url=#{escaped_url}").read  
  return short_url;  
 end  

Also if you have written a data query method to get start and end time from DB, then it is easy to get startArray and endArray directly by T-SQL. i am using this in practice: http://snippets.dzone.com/posts/show/3906

or you may take look at this, FYI:
 require 'win32ole'  
 class SQLServer  
   # This class manages database connection and queries  
   attr_accessor :connection, :data, :fields  
   attr_writer :username, :password  
   def initialize(host, username = 'sa', password='sa')  
     @connection = nil  
     @data = nil  
     @host = host  
     @username = username  
     @password = password  
   end  
   def open(database)  
     # Open ADO connection to the SQL Server database  
     connection_string = "Provider=SQLOLEDB.1;" #SQLOLEDB.1,SQLNCLI  
     connection_string << "Persist Security Info=False;"  
     connection_string << "User ID=#{@username};"  
     connection_string << "password=#{@password};"  
     connection_string << "Initial Catalog=#{database};"  
     connection_string << "Data Source=#{@host};"  
     connection_string << "Network Library=dbmssocn"  
     @connection = WIN32OLE.new('ADODB.Connection')  
     @connection.Open(connection_string)  
   end  
   def query(sql)  
     # Create an instance of an ADO Recordset  
     recordset = WIN32OLE.new('ADODB.Recordset')  
     # Open the recordset, using an SQL statement and the  
     # existing ADO connection  
     recordset.Open(sql, @connection)  
     # Create and populate an array of field names  
     @fields = []  
     recordset.Fields.each do |field|  
       @fields << field.Name  
     end  
     begin  
       # Move to the first record/row, if any exist  
       recordset.MoveFirst  
       # Grab all records  
       @data = recordset.GetRows  
     rescue  
       @data = []  
     end  
     recordset.Close  
     # An ADO Recordset's GetRows method returns an array  
     # of columns, so we'll use the transpose method to  
     # convert it to an array of rows  
     @data = @data.transpose  
   end  
   def close  
     @connection.Close  
   end  
 end  

Usage sample:
 db = SQLServer.new('YourDBIP', 'sa', 'sa')  
 sp = db.open('DBName')  
 startArray = db.query("select * from somewhere where ...")  
 endArray = db.query("sselect * from somewhere where ...")  
 db.close  

Monday, September 20, 2010

Handle Datepicker using WebDriver simple sample

Someone asked me: "How to identify a datepicker on the webpage, which will display after clicking the datepicker icon?"
Here is a simple sample, you can customize your own function for reading your web page source and make a api for picking any dates freely :-)

//Find all the Calendar on the Web Page
List<WebElement> Datepickers = driver.findElements(By.className("CalendarIcon"));

//Trigger the calendar of StartDate
driver.manage().timeouts().implicitlyWait(5, TimeUnit.SECONDS);
Datepickers.get(0).click();


WebElement table = driver.findElement(By.className("ui-datepicker-calendar"));

List<WebElement> tds = table.findElements(By.tagName("td"));
for (WebElement td: tds){
//Select 20th Date of current month
if (td.getText().equals("20")){
td.findElement(By.linkText("20")).click();
break;
}

}

//Trigger the calendar of EndDate
driver.manage().timeouts().implicitlyWait(5, TimeUnit.SECONDS);
Datepickers.get(1).click();

WebElement table2 = driver.findElement(By.className("ui-datepicker-calendar"));

List<WebElement> tds2 = table2.findElements(By.tagName("td"));
for (WebElement td2: tds2){
//Select 22th Date of current month
if (td2.getText().equals("22")){
td2.findElement(By.linkText("22")).click();
break;
}

}

Wednesday, September 15, 2010

Table walk through in WebDriver

Very Simple sample for dealing with table elements on webPage in WebDriver:


WebElement table = driver.findElement(By.name("meetingRoomTable"));
//findout all checkboxes within the table
List<WebElement> checkboxes = table.findElements(By.cssSelector("input[type=\"checkbox\"]"));
if (!checkboxes.isEmpty()){
for (WebElement checkbox : checkboxes){

if(!checkbox.isSelected()){
checkbox.toggle();
}
}
System.out.println("all checkboxes elements checked now");
}

else
System.out.println("no checkbox elements exsit on this table");

Sunday, September 12, 2010

Distribute your test suites by STAF(STAX)

STAF is a good Open source "test automation" framework which developed by IBM. I am currently using STAF(STAX) to distribute my test Suites which are organized by TestNG, it is improve your efficiency.

Here is a simple sample how I zip, transfer and execute My Test on 2 machines(please format this XML by yourself, if you need to look at it):

 <?xml version="1.0" encoding="UTF-8" standalone="no"?>  
 <!DOCTYPE stax SYSTEM "stax.dtd">  
 <!-- New document created with EditiX at Fri Jul 09 15:35:49 CST 2010 -->  
 <stax>  
 <script>machinelist = ['Machinename1','Machinename2']</script>  
 <defaultcall function="ZipTest"></defaultcall>  
 <function name="ZipTest">  
 <sequence>  
 <stafcmd>  
 <location>'localhost'</location>  
 <service>'zip'</service>  
 <request>'ADD ZIPFILE D:/Raft_Project/TestPlan.zip DIRECTORY D:/Raft_Project/TestPlan RECURSE RELATIVETO D:/Raft_Project' </request>  
 </stafcmd>  
 <call function="'sleep'"></call>  
 <call function="'DistributionTest'"></call>  
 <call function="'sleep'"></call>  
 <call function="'CleanupTest'"></call>  
 </sequence>  
 </function>  
 <function name="DistributionTest">  
 <paralleliterate var="machinename" in="machinelist">  
 <sequence>  
 <stafcmd>  
 <location>machinename</location>  
 <service>'fs'</service>  
 <request>'DELETE ENTRY D:/Raft_Project/TestPlan RECURSE CONFIRM' </request>  
 </stafcmd>  
 <call function="'sleep'"></call>  
 <stafcmd>  
 <location>'local'</location>  
 <service>'fs'</service>  
 <request>'COPY FILE D:/Raft_Project/TestPlan.zip TODIRECTORY D:/Raft_Project/ TOMACHINE %s' % machinename </request>  
 </stafcmd>  
 <call function="'sleep'"></call>  
 <stafcmd>  
 <location>machinename</location>  
 <service>'zip'</service>  
 <request>'UNZIP ZIPFILE D:/Raft_Project/TestPlan.zip TODIRECTORY D:/Raft_Project/'</request>  
 </stafcmd>  
 <call function="'sleep'"></call>  
 <stafcmd>  
 <location>machinename</location>  
 <service>'fs'</service>  
 <request>'DELETE ENTRY D:/Raft_Project/TestPlan.zip RECURSE CONFIRM'</request>  
 </stafcmd>  
 <process>  
 <location>machinename</location>  
 <command mode='"shell"'>'Runner.bat'</command>  
 <workdir>R'D:\Raft_Project\TestPlan\TestRunner_Module1'</workdir>  
 <returnstdout/>  
 <returnstderr/>  
 </process>  
 </sequence>  
 </paralleliterate>  
 </function>  
 <function name="CleanupTest">  
 <stafcmd>  
 <location>'localhost'</location>  
 <service>'fs'</service>  
 <request>'DELETE ENTRY D:/Raft_Project/TestPlan.zip RECURSE CONFIRM'</request>  
 </stafcmd>  
 </function>  
 <function name="sleep">  
 <stafcmd>  
 <location>'localhost'</location>  
 <service>'delay'</service>  
 <request>'delay 6000'</request>  
 </stafcmd>  
 </function>  
 </stax>  
hope it helps! More detail instruction, please go to STAF official website:http://staf.sourceforge.net/

PS: you may need to modify your under STAF/bin/, so that it can avoid some restrictions.
 # Turn on tracing of internal errors and deprecated options  
 trace enable tracepoints "error deprecated"  
 # Enable TCP/IP connections  
 interface ssl library STAFTCP option Secure=Yes option Port=6550  
 interface tcp library STAFTCP option Secure=No option Port=6500  
 # Set default local trust  
 #trust machine local://local level 5  
 trust default level 5  
 # Add default service loader  
 serviceloader library STAFDSLS  
 SERVICE STAX LIBRARY JSTAF EXECUTE \  
 D:/STAF/services/stax/STAX.jar OPTION J2=-Xmx384m  
 SERVICE EVENT LIBRARY JSTAF EXECUTE \  
 D:/STAF/services/stax/STAFEvent.jar  
 SET MAXQUEUESIZE 10000  
 SERVICE Cron LIBRARY JSTAF EXECUTE D:\STAF\services\cron\STAFCron.jar  

WebDriver Wait is easier after using implicitlyWait()

"WebDriver will wait until the page has fully loaded (that is, the "onload" event has fired) before returning control to your test or script."
The classic example is Javascript starting to run after the page has loaded (onload); while if the website is very "rich", it is always hard for WebDriver to identify some elements by its own blocking API.

Previous solution mentioned/suggested by experts are : Use the Wait class to wait for a specific element to appear:http://groups.google.com/group/webdriver/browse_thread/thread/50c963cd25fb416c/61aae67b8a2560fd?#61aae67b8a2560fd

Here is one example, which i wrapped the presenceOfElementLocated():

 import org.openqa.selenium.By;  
 import org.openqa.selenium.WebDriver;  
 import org.openqa.selenium.WebElement;  
 import org.openqa.selenium.support.ui.WebDriverWait;  
 import com.google.common.base.Function;  
 public class MyWaiter {  
      private WebDriver driver;  
      public MyWaiter(WebDriver driver){  
           this.driver = driver;  
      }  
      public WebElement waitForMe(By locatorname, int timeout){  
           WebDriverWait wait = new WebDriverWait(driver, timeout);  
           return wait.until(MyWaiter.presenceOfElementLocated(locatorname));  
      }  
      public static Function<WebDriver, WebElement> presenceOfElementLocated(final By locator) {  
           // TODO Auto-generated method stub  
           return new Function<WebDriver, WebElement>() {  
                @Override  
                public WebElement apply(WebDriver driver) {  
                     return driver.findElement(locator);  
                }  
           };  
      }  
 }  

My test code:
 public static void main(String[] args) {  
           WebDriver driver = new FirefoxDriver();  
           driver.get("http://www.google.com/");  
           MyWaiter myWaiter = new MyWaiter(driver);  
           WebElement search = myWaiter.waitForMe(By.name("btnG"), 10);  
           search.click();  
      }  

This way looks a little bit fussy to me, although it works well.

Now, implicitlyWait() gives tests a "KISS" and most importantly, it did solve the problem.

driver.findElement(By.id("signIn")).click();  
driver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS);  
driver.switchTo().frame("canvas_frame");  
driver.findElement(By.partialLinkText("Buzz")).click();  
driver.findElement(By.linkText("Cheng Chi")).click();  


You may notice that the first sample code using wait.until(), you have to two wait.until() for each link element, otherwise the code will fail; While the second one, you just add one implicitlyWait() in front of the "Buzz" link.
I do not know why, but it shows that implicitlyWait() is a better choice for locating (ajax) elements.

PS: I marked "in front of" as bold above, which means implicitlyWait() is a kind of registration method, you need to tell Webdriver in advance.

Thursday, September 02, 2010

Check and Set Isolation level in SQL Server

Check isolation level:
DBCC useroptions


Default Isolation level in SQL Server is "READ COMMITTED", Once you want to change it:

For example, Set isolation level to READ UNCOMMITTED
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED