Showing posts with label pattern. Show all posts
Showing posts with label pattern. Show all posts

Monday, June 04, 2012

Good Practice: How to store the configuration of your Java-based testing framework. Part I.

Some day your framework (or any other somehow complicated project) will become so big that it will not be convenient to keep the configuration hard-coded or even in the property files. I'm probably saying not even about the configuration in regular meaning but rather about project-specific meta-information. 

What values are we looking for...

You will probably need to store:

  • Credentials
  • Some steps to perform on some stage
  • Resources and some extra attributes for them
  • Connection settings for set of databases
  • Rules for parsing certain data
  • etc.
All this stuff requires certain capabilities from your configuration storage. If you'd like to have easy usage you'd probably expect the following features from that:
  • Nice to keep all such the information in the minimum of files
  • Nice if your storage supports structural data
  • Nice if you have convenient and effective facility to read and write data out of/in that storage
  • Nice if such the format is highly standardized
  • Nice if such the format has the facility to validate the stored data for at least syntax.
  • Great if such the format won't require extra effort for infrastructure setting-up from you
  • Great if the storage keeps the data in the format which can be understood by the human
  • Great if the format allows applying of different IDEs to introduce changes in stored data manually

XML - nice alternative

Well if to take a look onto the items above the two ideas comes on my mind. First one - the idea which addresses all the Nice items. This idea is certainly relational database. However it is hard and heavy solution and may cause pretty annoying problems related to the connectivity. 
Another way is both Nice and Great. I'm talking about storing your meta-information in xml file. If you're going to use xml in your project you would probably like to apply good practice of such the pattern.

If you decided to use xml to store your data it's great to have the understanding of which exactly data you're going to store there. The very good way to understand it and to keep acting according that understanding is to design the xml-schema which does actually represent the model of data you're going to store and use.
Once you have such the schema you'll be able not to understand and troubleshoot any data-structure-related problems of your project but also will be able to use such the sweet features like validating your files against that schema and using dedicated IDEs so that you'll have the code assistance feature enabled.

Xml-schema is the sort of grammar constraints of the language your going to describe your project's meta-information with. Applying such the grammar to your configuration file will allow to catch wide set of improper data problems until your main code gets executed. 

Here you may find small but effective tutorial on how to understand and to use the schema file.

Later on I'll tell how to use all the features I told above for the real example. 

Wednesday, April 18, 2012

Useful regular expression: how to verify perforce branch path

Here is the regular expression (aka regexp) pattern to verify if the input string matches the rules of perforce branch specification (the string should look like this: //depot/level1/level2/level3/... )
//(?:\w+?/)+?(?:\.\.\.$)
 Symbols ?:  mean that we would not like to capture them (does not make sense for match function of regular expression but still reasonable to make the logic of the pattern clear)/ 

Wednesday, March 21, 2012

Ant practice on how to build your file with substituing the @tokens@

The usual need of a person going to build the code is to keep the certain properties in separate file and move the property values to certain files on build stage. This is what the article about. Let's look and the very small example.

Assume we have the following property file (let's name it "configuration properties")
user.role=admin
user.login=superuser
user.password=whoifnotme

and the following file we're going to build (settings.xml)
<settings>
 <users>
  <user id="@user.login@">
   <role>@user.role@</role>
   <password>@user.password@</password>
  </user>
 </users>
</settings>

Let's place the files above to d:/temp/props_src and would like to build the file settings.xml to the folder d:/temp/props_build.

Here is how the ant script should look like

<project name="testBuildCodeWithProperties" default="start" basedir=".">

 <property name="sourceDir" value="d:/temp/props_src"/>
 <property name="targetDir" value="d:/temp/props_build"/>
 
 <target name="start">
  <copy todir="${targetDir}" overwrite="true">
   <fileset dir="${sourceDir}" includes="*.xml"/>
   <filterset filtersfile="${sourceDir}/configuration.properties"/>
  </copy>
 </target>
 
</project>

Note that we should not write any specific script to substitute the values. The only inbound data we need to have to build the file is actually the template of a file to build and the property file to be used in filter set. The @ symbol is used by default to point out the tokens in the template file. However you may change it by specifying begintoken and endtoken attributes of filterset.

Friday, December 02, 2011

Antipatterns

Very interesting article here. Personally I found a lot of so called anti patterns being used in my work. That's the chance to change my mind and change my efficiency. Probably yours as well :)

Wednesday, November 30, 2011

Another robust way of how to locate ajax elements using Selenium 2 + webdriver

Check new series of the articles. Review and user experience on test management systems. Functionality and usability.
-------------------

Once you're going to test somehow complicated web-site you should investigate the way of what to consider as the succeess of your another action?

As it looks to me we have two kinds of success indicators of our action on UI
1. You boserve the element that is expected to be displayed (ex.: you're clicking the button and expect to see the dialog)
2. You do not observe the element that is not expected to be displayed. (ex.: you're closing the dialog and expect it disappears)

Also you should take into account that the elements on your page may appear and disappear without page re-loading. That's why I'd like to suggest the following design addressing such the points:

UPD: Here is the sophisticated and of-good-practice way of how to to that.

Introduce two methods:

public WebElement lookupXPathExists(String xpath) throws MyAutomationException{
  WebElement handledElement = repeatableLookupExists(xpath);
  if (handledElement!=null) {
   return handledElement;
  } else {
   throw new MyAutomationException("Lookup for element ["
     + xpath + "]" + " failed after "
     + AWAITING_THRESHOLD_MS + " ms awaiting.");
   
  }
 }
 
 public boolean lookupXPathDoesNotExist(String xpath) throws MyAutomationException {
  if (repeatableLookupDoesNotExist(xpath)) {
   return true;
  }else{
   throw new MyAutomationException("The xpath ["+xpath+"] is still observed after "+AWAITING_THRESHOLD_MS + " ms awaiting.");
  }
 }

Where AWAITING_THRESHOLD_MS is static final variable holding the period you'd like to wait until the element will appear. The first method returns the element found using your locator. The second one just checks if the element you're searching does not present on the page. Both of them call their repeatable helpers which are shown below:

private void validateFoundElements(List<webelement> elementList) throws SeleniumException{
  
  MyTestCase.logger.debug(testCase.wrapMessage("Validating found elements..."));
  
  if(elementList.isEmpty()){
   throw new SeleniumException("Cant find specified element..");
  }
  
  if(countVisibleItems(elementList) > 1){
   throw new SeleniumException("Specified xpath found several elements. Please concretize.");
  }
 }
 
 private int countVisibleItems(List<webelement> elementList){
  int visibleItemsNumber = 0;
  for(WebElement element: elementList){
   if(element.isDisplayed()){
    visibleItemsNumber++;
   }
  }
  return visibleItemsNumber;
 }
 
 private WebElement repeatableLookupExists(String xpath) {
  long start = System.currentTimeMillis();
  while (true) {
   try{
    List<webelement> listOfFoundElements = driver.findElements(By.xpath(xpath));
    validateFoundElements(listOfFoundElements);
    if(driver.findElement(By.xpath(xpath)).isDisplayed()){
     MyTestCase.logger.debug(testCase.wrapMessage("Found xPath [" + xpath + "]. Creating object.."));
     return driver.findElement(By.xpath(xpath));
    }
   }catch(SeleniumException se){
    try {
     Thread.sleep(AWAITING_UNIT_LENGTH_MS);
     MyTestCase.logger.debug(testCase.wrapMessage("Searching xPath [" + xpath + "]. Retry.."));
    } catch (InterruptedException e) {
     e.printStackTrace();
    }
   }
   if (System.currentTimeMillis()-start > AWAITING_THRESHOLD_MS){
    break;
   }
  }
  Statistics.totalLatency += System.currentTimeMillis() - start;
  return null;
 }

 private boolean repeatableLookupDoesNotExist(String xpath) {
  long start = System.currentTimeMillis();
  while (true) {
   boolean isPresent = driver.findElements(By.xpath(xpath)).isEmpty() ? false : true;
   if (!isPresent) {
    Statistics.totalLatency += System.currentTimeMillis() - start;
    MyTestCase.logger.debug(testCase.wrapMessage("Not found xPath [" + xpath + "]. Success.."));
    return true;
   }
   else{
    if(countVisibleItems(driver.findElements(By.xpath(xpath)))==0){
     MyTestCase.logger.debug(testCase.wrapMessage("Found xPath [" + xpath + "]. However it is not visible. Success.."));
     return true;
    }
    try {
     Thread.sleep(AWAITING_UNIT_LENGTH_MS);
     MyTestCase.logger.debug(testCase.wrapMessage("XPath [" + xpath + "] still can be found. Retry.."));
     if (System.currentTimeMillis()-start > AWAITING_THRESHOLD_MS){
      break;
     }
    } catch (InterruptedException e) {
     e.printStackTrace();
    }
   }
  }
  Statistics.totalLatency += System.currentTimeMillis() - start;
  return false;
 }

Where AWAITING_UNIT_LENGTH_MS is final variable saying how often you'd like to check if the element appeared/disappeared.
That is how the solution work. The only thing you should always remember about is that once you want to check if the element is not present on the page and you realize that it isn't you should make sure it is not visible on the page because of the proper working of the tested product but not because of the page hasn't been completely loaded. To make that sure try to determine the checking element that should be obligatory visible while that one you do not expect to see isn't. So use the following pattern to ensure the element is not visible as expected:
1. lookupXPathExists("xpath of the element saying the page is loaded")
2. lookupXPathDoesNotExist("xpath you're expecting not to see on the page").

That is it.

Wednesday, August 24, 2011

P4 Java API. How to work with temporary clients.

Check new series of the articles. Review and user experience on test management systems. Functionality and usability.
-------------------

There is not a lot of information about perforce Java API. However you may find the examples of how to apply the common use cases in your code. I tried to touch surrounding cases and faced the problem that had been resolved with the only decompilation help.

So assume you do not want to use the existing client and you do want to use the temporary one. How to address such the requirement. Here is the code from me.


package ar.p4apihelpers;

import java.io.IOException;
import java.net.URISyntaxException;
import java.util.List;
import java.util.UUID;

import com.perforce.p4java.client.IClient;
import com.perforce.p4java.core.IMapEntry.EntryType;
import com.perforce.p4java.core.file.FileSpecBuilder;
import com.perforce.p4java.core.file.IFileSpec;
import com.perforce.p4java.exception.P4JavaException;
import com.perforce.p4java.impl.generic.client.ClientView;
import com.perforce.p4java.impl.generic.client.ClientView.ClientViewMapping;
import com.perforce.p4java.impl.mapbased.client.Client;
import com.perforce.p4java.server.IServer;
import com.perforce.p4java.server.IServerInfo;
import com.perforce.p4java.server.ServerFactory;

public class P4APISyncUp{
 public static void main(String[] args) throws URISyntaxException, IOException, P4JavaException {
  // Generating the files to sync-up
  String[] pathsUnderDepot = new String[]{
    "//depot/path1/file1.java#head",
    "//depot/path1/file2.java#head",
    "//depot/path1/path2/file1.java#head"
  };
  // Instantiating the server
  IServer p4Server = ServerFactory.getServer("p4java://p4.fakeserver.com:1666", null);
  p4Server.connect();
  // Authorizing
  p4Server.setUserName("secretname");
  p4Server.login("secretpassword");
  // Just check you are connected successfully
  IServerInfo serverInfo = p4Server.getServerInfo();
  System.out.println(serverInfo.getServerLicense());
  // Creating new client
  IClient tempClient = new Client();
  // Setting up the name and the root folder
  tempClient.setName("tempClient" + UUID.randomUUID().toString().replace("-", ""));
  tempClient.setRoot("c:/temp");
  tempClient.setServer(p4Server);
  // Setting the client as the current one for the server
  p4Server.setCurrentClient(tempClient);
  // Creating Client View entry
  ClientViewMapping tempMappingEntry = new ClientViewMapping();
  // Setting up the mapping properties
  tempMappingEntry.setLeft("//depot/...");
  tempMappingEntry.setRight("//" + tempClient.getName() + "/...");
  tempMappingEntry.setType(EntryType.INCLUDE);
  // Creating Client view
  ClientView tempClientView = new ClientView();
  // Attaching client view entry to client view
  tempClientView.addEntry(tempMappingEntry);
  tempClient.setClientView(tempClientView);
  // Registering the new client on the server
  System.out.println(p4Server.createClient(tempClient));
  // Surrounding the underlying block with try as we want some action
  // (namely client removing) to be performed in any way 
  try{
   // Forming the FileSpec collection to be synced-up
   List<ifilespec> fileSpecsSet = FileSpecBuilder.makeFileSpecList(pathsUnderDepot);
   // Syncing up the client
   tempClient.sync(FileSpecBuilder.getValidFileSpecs(fileSpecsSet), true, false, false, false);
  }finally{
   // Removing the temporary client from the server
   System.out.println(p4Server.deleteClient(tempClient.getName(), false));
  }
 }
}


Please point the attention to the lines #42 and #50 of this code. Forgetting to call #42 leads to the following exception on execution #64:
Exception in thread "main" java.lang.NullPointerException
at com.perforce.p4java.impl.mapbased.client.Client.sync(Client.java:496)
at com.perforce.p4java.impl.mapbased.client.Client.sync(Client.java:477)
at ar.p4apihelpers.P4APISyncUp.main

If you forget to call #50 you will get the exception like this:
Exception in thread "main" com.perforce.p4java.exception.RequestException: Error in client specification.
Error detected at line 10.
Null directory (//) not allowed in 'null//depot/...'.

at com.perforce.p4java.impl.mapbased.server.Server.handleErrorStr(Server.java:3834)
at com.perforce.p4java.impl.mapbased.server.Server.createClient(Server.java:2099)
at ar.p4apihelpers.P4APISyncUp.main

Friday, July 08, 2011

Custom way to look-up ajax elements in Selenium. Pattern.

Hi!

Looking through a lot of forums I noticed that some people face problems when use native selenium instructions to wait for element appearance on the page. That become pretty big problem when we're talking about dynamic HTML content. In my practice I met the close problems and the way I resolved them is the following pattern. This class controls the look-up procedures on the page. I designed to be capable to configure the duration and number of repeat lookups depending on the channel bandwidth and other factors. Also it has the method allowing us to wait while the required element disappears. That is pretty much common situation in web testing as well. So the class looks like this:


UPD: Here is the sophisticated and of-good-practice way of how to to that in Selenium 2.


package ar.example;

import java.util.HashMap;
import java.util.Properties;

import com.thoughtworks.selenium.DefaultSelenium;
import com.thoughtworks.selenium.Selenium;
import com.thoughtworks.selenium.SeleniumException;

public class SeleniumAdapter {
 
 private final int AWAITING_UNIT_LENGTH_MS = 300;
 private final long AWAITING_THRESHOLD_MS = 10000;
 private Selenium driver;
 

    public static String getRelativePath(String testedDomain, String alteredPath){
   return testedDomain + alteredPath;
 }
 
 public SeleniumAdapter(Properties properties) {
  driver = new DefaultSelenium(properties.getProperty("host"),  Integer.parseInt(properties.getProperty("port")), properties.getProperty("browser"), properties.getProperty("domain"));
  driver.start();
  driver.open(properties.getProperty("domain"));
  driver.setSpeed("500");
 }
 
 public void closeDriver(){
  driver.stop();
 }
  
 public boolean ifXPathExists(String xpathExpression){
  try {
   lookupXPathExists(xpathExpression);
  } catch (AutomationException e) {
   return false;
  }
  return true;
 }
  
 private WebElementEmulator repeatableLookupExists(String xpath) {
  long start = System.currentTimeMillis();
  while (true) {
   try{
    if(driver.isVisible("xpath=" + xpath)){
     TTestCase.logger.debug(testCase.wrapMessage("Found xPath [" + xpath + "]. Creating object.."));
     return new WebElementEmulator(xpath, driver);
    }
   }catch(SeleniumException se){
    try {
     Thread.sleep(AWAITING_UNIT_LENGTH_MS);
     TTestCase.logger.debug(testCase.wrapMessage("Searching xPath [" + xpath + "]. Retry.."));
    } catch (InterruptedException e) {
     e.printStackTrace();
    }
   }
   if (System.currentTimeMillis()-start > AWAITING_THRESHOLD_MS){
    break;
   }
  }
  Statistics.totalLatency += System.currentTimeMillis() - start;
  return null;
 }

 private boolean repeatableLookupDoesNotExist(String xpath) {
  long start = System.currentTimeMillis();
  while (true) {
   boolean isPresent = driver.isElementPresent("xpath="+xpath);
   if (!isPresent) {
    Statistics.totalLatency += System.currentTimeMillis() - start;
    TTestCase.logger.debug(testCase.wrapMessage("Not found xPath [" + xpath + "]. Success.."));
    return true;
   }
   else{
    if(!driver.isVisible("xpath="+xpath)){
     TTestCase.logger.debug(testCase.wrapMessage("Found xPath [" + xpath + "]. However it is not visible. Success.."));
     return true;
    }
    try {
     Thread.sleep(AWAITING_UNIT_LENGTH_MS);
     TTestCase.logger.debug(testCase.wrapMessage("XPath [" + xpath + "] still can be found. Retry.."));
     if (System.currentTimeMillis()-start > AWAITING_THRESHOLD_MS){
      break;
     }
    } catch (InterruptedException e) {
     e.printStackTrace();
    }
   }
  }
  Statistics.totalLatency += System.currentTimeMillis() - start;
  return false;
 }

 public WebElementEmulator lookupXPathExists(String xpath) throws AutomationException{
  WebElementEmulator handledElement = repeatableLookupExists(xpath);
  if (handledElement!=null) {
   if (handledElement.getQuantity()>1){
    throw new AutomationException("Several elements can be located using specified xpath [" 
      + xpath + "] You should concretize.");
   }
   return handledElement;
  } else {
   throw new AutomationException("Lookup for element ["
     + xpath + "]" + " failed after "
     + AWAITING_THRESHOLD_MS + " ms awaiting.");
   
  }
 }
 
 public boolean lookupXPathDoesNotExist(String xpath) throws AutomationException {
  if (repeatableLookupDoesNotExist(xpath)) {
   return true;
  }else{
   throw new AutomationException("The xpath ["+xpath+"] is still observed after "+AWAITING_THRESHOLD_MS + " ms awaiting.");
  }
 }
}

That's pretty much it.

Tuesday, July 05, 2011

How to get instant updates of configuration file of your testing framework from svn repository

Hi.

Here is one more pattern intended for operating with the latest versions of configuration files. That was originated from the following. The project under testing behaves pretty much agile so to hold such agile things up to date we want to extract the relevant configs from some centralized storage that is easy to maintain.
To support such functionality I prepared small utility class to communicate with svn repository. The pattern looks like this:

P.S. - you should use svnkit in order to keep the code working.

package test.svn.client;

import java.io.File;

import org.tmatesoft.svn.core.SVNDepth;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.SVNURL;
import org.tmatesoft.svn.core.auth.ISVNAuthenticationManager;
import org.tmatesoft.svn.core.internal.io.dav.DAVRepositoryFactory;
import org.tmatesoft.svn.core.internal.io.svn.SVNRepositoryFactoryImpl;
import org.tmatesoft.svn.core.io.SVNRepository;
import org.tmatesoft.svn.core.wc.SVNClientManager;
import org.tmatesoft.svn.core.wc.SVNRevision;
import org.tmatesoft.svn.core.wc.SVNUpdateClient;
import org.tmatesoft.svn.core.wc.SVNWCUtil;

public class Communicator {
 
 static final String syncDest = "/Storage";
 
 SVNUpdateClient uc;
 SVNURL repositoryURL;
 
 public Communicator(String user, String password, String repository) throws SVNException{
  repositoryURL = SVNURL.parseURIEncoded(repository);
  DAVRepositoryFactory.setup();
  SVNRepository svnRepository = SVNRepositoryFactoryImpl.create(repositoryURL);
  ISVNAuthenticationManager manager = SVNWCUtil.createDefaultAuthenticationManager(user, password);
  svnRepository.setAuthenticationManager(manager);
  SVNClientManager cm = SVNClientManager.newInstance();
  cm.setAuthenticationManager(manager);
  uc = cm.getUpdateClient();
 }
 
 public void syncUp(String segment) throws SVNException{
  uc.doCheckout(repositoryURL.appendPath(segment, true), new File(syncDest), SVNRevision.UNDEFINED, SVNRevision.HEAD, SVNDepth.INFINITY, true);
  System.out.println("Done");
 }
 
 public String getResourceFolder(){
  return syncDest;
 }
 
 /**
  * Just to check how it really works :)
  * @param arg
  * @throws SVNException
  */
 public static void main(String[] arg) throws SVNException{
  Communicator driver = new Communicator("secret", "secret", "https://my.svnserver.fake/trunk");
  driver.syncUp("/resources/config");
 }
}

Then we should write the support for fetching the certain values from the resource files

package com.somefake.pkg;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.InvalidPropertiesFormatException;
import java.util.Properties;

import com.somefake.pkg.TAE;

public class ResourceProvider {
 
 private static final Communicator resourceActualyzer = new Communicator("testuser", "testpassword", "https://fakesvnserver.com/trunk");
 
 private static Locale currentLocale = Locale.RU;
 
 private static final HashMap<Locale, Properties> map = new HashMap<Locale, Properties>();
 
 
 static{
  TipsteryTestCase.logger.info("Initializing SVN client...");
  try {
   HashSet<String> localeRu = new HashSet<String>();
   HashSet<String> localeEn = new HashSet<String>();
   localeRu.add(resourceActualyzer.getResourceFolder() + "/Resources_ru.xml");
   localeRu.add(resourceActualyzer.getResourceFolder() + "/jsResources_ru.xml");
   localeEn.add(resourceActualyzer.getResourceFolder() + "/Resources.xml");
   localeEn.add(resourceActualyzer.getResourceFolder() + "/jsResources.xml");
   resourceActualyzer.syncUp("/resources");
   map.put(Locale.RU, loadProperties(localeRu));
   map.put(Locale.EN, loadProperties(localeEn));
  } catch (Exception e) {
   resourceActualyzer.setSuccessful(false, e);
   TipsteryTestCase.logger.error("Error while loading resources.", e);
  }
 }
 
 private static Properties loadProperties(HashSet<String> sourceFiles) throws InvalidPropertiesFormatException, FileNotFoundException, IOException{
  Properties stagingProperties = new Properties();
  for(String sourceFile: sourceFiles){
   stagingProperties.loadFromXML(new FileInputStream(sourceFile));
  }
  return stagingProperties;
 }
 
 public static String getProperty(String key) throws TAE{
  if(!resourceActualyzer.isSuccessful()){
   String message = "Looks like you had problems with " 
     + "resource files syncronization\n"
     + "Check if you set valid credentials and specified valid resource file names";
   TipsteryTestCase.error(message);
   resourceActualyzer.getException().printStackTrace();
   throw new TAE(message);
  }
  return map.get(currentLocale).getProperty(key);
 }
 
 public static void setLocale(Locale newLocale){
  currentLocale = newLocale;
 }
 
 /**
  * Just to check if it works
  * @param arg
  * @throws TAE 
  */
 public static void main(String[] arg) throws TAE{
  System.out.println(ResourceProvider.getProperty("some.property"));
  ResourceProvider.setLocale(Locale.EN);
  System.out.println(ResourceProvider.getProperty("some.property"));
 }
}

Locale here means just the enum, and do not curse me for the approach to the errors handling.

Wednesday, June 22, 2011

How to hide some data in log4j log you consider to be secured

Once we get the requirement saying the customer does not want some data gets transfered to the log file since it looks secure for them. We even should have the capability of choosing whether to show such data in logs. The quick way to address the requirement is to override one method in PatternLayout class. This will look like:

import org.apache.log4j.PatternLayout;
import org.apache.log4j.spi.LoggingEvent;

public class SecureLayout extends PatternLayout{

                static String pattern;
                
                public static void setPattern(String pattern){
                                SecureLayout.pattern = pattern;
                }

                @Override
                public String format(LoggingEvent event) {
                                String string = super.format(event);
                                return pattern == null ?  string : string.replaceAll(pattern, "NotForYourEyes");
                }

}

Such the way will allow us to set which data we consider to be secured from any place of the code. So you should only place the class under the classpath and use the following construction in log4j.xml

    <appender name="FILE" class="org.apache.log4j.RollingFileAppender">
        <param name="File" value="logfile.log"/>
        <param name="Append" value="true"/>
        <param name="MaxFileSize" value="1000KB"/>
        <param name="MaxBackupIndex" value="1000"/>
        <layout class="some.package.SecureLayout">
            <param name="ConversionPattern" value="%-5p [%d{ISO8601}] - %m%n"/>
        </layout>
    </appender>

However such solution of described engineering problem has some drawback. It means we are not capable to hide the data in exception description as exceptions are handled in another way. However that problem can be solved by introducing the changes in Exception class.

Friday, June 17, 2011

How to check if sub-string belongs to the string in batch script

Check new series of the articles. Review and user experience on test management systems. Functionality and usability.
-------------------

Recently I was in need to prepare the batch file taking the sting and some pattern as the parameters. They required that file to check if that string contains the certain substring and basing on the result execute either one functionality or another. So the solution is below:

rem first parameter should hold the main string
rem second one should hold the substring to be found

SETLOCAL ENABLEDELAYEDEXPANSION 
set "ol=%1"
set handledString=!ol:%2=!
echo."%handledString%"
IF %1 == %handledString% GOTO wayone
IF %1 NEQ %handledString% GOTO waytwo

:wayone
echo."substring not found"
goto end

:waytwo
echo."substring found"
goto end

:end

UPD: Here is more sophisticated way how to do this.

Thursday, May 05, 2011

QA team in scrum methodology

The more I'm involved in SCRUM development process the stronger I'm sure QA team has to be formed as some kind of service department. Some kind of team reporting to other ones. The same process (like cards, planning poker, etc.) but the only difference will be the consumer of "QA product". The product of the QA team will be consumed by the teams performing actual development.
The back-log will be filled with the stories like "I want to have the testplan for the functionality". That will make the product owners create detailed requirements earlier than usual. Once the iteration "n" will be ready and the test plan will be prepared, the team will be able to handle the story "I want the functionality is verified according to prepared test plan" in iteration "n+1".
Such approach will help to accumulate and consolidate QA resources within one administrative unit and use the collaborated knowledge of the overall resource.

Monday, February 14, 2011

Pattern to measure the time of execution

It often happens that you need the way to measure the time of certain step execution in your scenarios or just your own scripts. What is the convenient way of doing this? The practice I use is to wrap the steps with ad-hoc class that is declared abstract and having the following structure:


This way will allow you to manage the measurement aspects without the necessity of all the code refactoring. Here is the example of how to use this approach:
Here it is presented how to measure the certain step's execution time. We should instantiate that abstract class right within the code to be measured so it will be wrapped with step() method implementation for each required part of code.
The provided example shows how I measured some files.