Tuesday, November 20, 2018

Enable Continuous Testing in DevOps environment


Introduction

This is a pretty standard scenario from the quality engineering perspective where all the organization are trying to move towards DevOps. Organization have conventionally been doing automated functional and regression testing but with DevOps a change in approach towards continuous testing across DevOps phases is very important. In this small write-up I am trying to present an approach how teams/testing organization can move towards enabling continuous testing in DevOps.

Change of approach and mindset

I strongly believe that this change is driven more by approach and mindset rather than technological changes required as teams have been automating since many year but with emergence of DevOps we need to look at things in a different way. In my opinion below six mindset changes are import to be successful for CT enablement:

1.      Engage QE teams early: This is an important practice where QE teams are involved from the planning phase. This is done by following combination of processes and tools such as: TDD, BDD, static code analysis, code walkthrough, pair programming, mandatory unit test coverage etc.

2.      Be ready to discard: This is another important lesson where we need to change our mindset. In conventional automation approach we look at the ROI in the long run whereas that might not be a case here. In the fast agile there are instances where you may or may not use automation for longer time but at the same time is import to use automated testing to validate scenarios. For example validating huge data files, some intermediate changes to data which may not be there in further releases. In such scenario we should be ready to adapt mindset of developing, using and discarding scripts quickly. It does not always mean that everything we have built needs to be discarded but discard quickly as a possibility needs to be taken into consideration.

3.      Follow testing pyramid: Testing pyramid needs to be followed religiously thereby cutting down no of UI scenarios to minimum. This would help to create a more robust automation suite with less maintenance and would provide a better coverage and faster testing feedback to developers.

4.      Automate early: Focus needs to be on more automation. With increasing complexity and amount of data, it makes sense to automate test cases. It also helps to test features often and quickly. Other important thing is to automate early. This is where practices such as TDD and BDD comes into pictures and test are developed along with development and in some cases even before development has started.

5.      Service Virtualization: Virtualization helps to test component more comprehensively even when other system is not available or is not developed yet. This would help speed up testing giving more confidence to developers.

6.      Test more often and quickly: Test as often as possible – with every local build, with every new changes added, and in different environments. Including multiple quality gates and having more test coverage helps to build more reliable products and reduces chances of defect slippage in a higher environment.

Testing activities in DevOps Phases

Next step would be to understand different testing activities which are required to be performed in different DevOps phases. These can be broadly categorized as below:

1.      Planning Phase: This phase would involve creation of user stories, UACs, and coming up with solutions for the user stories. Static testing techniques such as review can be used to provide feedback in planning phase.

2.      Development and Integration: This is the phase where developers would develop features and integrate the code. Validation such as unit tests and checkstyles need to be performed at this stage to make sure that code changes are not breaking anything existing or have any conflict with any other developer who is also working on the same branch.  Developers also use this phase to perform component testing of their development and virtualization can play a key role to help developers to test their changes and get more confidence.

3.      Building the code: As soon as developer checks in his/her changes based on check-in or feature branch merge the build would be triggered. This build trigger is according to the branching strategy deployed. At this point changes might either be pushed to a container image or to an environment. At this point it becomes critical to ensure that changes pushed in are not breaking anything and unit test coverage is used as a build breaker to ensure that at least at the code level things are good.

4.      Deploy and deliver: Once build is done, it is deployed in the environment and functional changes are required to be tested. All the conventional test phases comes into picture in this phase where functional tests and regression tests are performed to ensure new features are working fine and old features are intact. Functional/regression tests are used in this phase to ensure build is fine and ready to be deployed. After tests are good, build is deployed to higher environment.
You can see from above phases that same phases are automated using orchestration tools such as Jenkins and other automation tools are used to perform different activities in different phase. As the process is automated, testing is all the more important to give confidence before deploying a build and automation testing will play a critical role to perform validations across different phases in a seamless manner.

Create a continuous testing maturity model

Start with creating a basic model to represent continuous testing maturity. This will help you assess and standardize your projects and come up with a roadmap. For example: have different levels such as: Beginner, Intermediate, Advances and have set of questions associated with each level. Questions may be:
a.      Do you have automated testing in place for functional testing?
b.      Have you automated your API tests?
c.      Do you engage QE early in the development cycle?
d.      Are you following agile methodology?
e.      Do you plan to adapt DevOps? If so, where are you in the journey right now?
f.       Do you automate unit tests?
g.      Do you use checkstyles in your code?
h.      Do you follow TDD/BDD etc?
Based on answers of above questions, you may evaluate your projects and come up with a maturity level. By no means above list is exhaustive or the levels I have defines are sufficient. But this can be a good starting point to come up with your own maturity model for continuous testing.

Approach towards continuous testing in DevOps

1.      Assess current status: Use maturity model defined above to evaluate state of the project and come up with a maturity for each project.
2.      Identify gaps: Based on maturity level come up with a gap analysis report to help you understand the current state.
3.      Create transformation roadmap and strategy: Based on the gaps create a transformation roadmap which will help you to define strategy how you can make changes and integrate to DevOps cycle thereby provide value.
4.      Implement transformation strategy: Time to implement the strategy which you have defined in step 3.

I have tried to list down the approach at a very high level for transformation. I would love to hear what you think about this.

Tuesday, June 10, 2014

Automation of Silverlight application using QTP - typical object identification issue

I am trying to consolidate the typical issues I have faced during Microsoft Silverlight v5.1 using QTP 11. I have tried to write generic functions so that it can be used as part of automation framework.

1. Scenario: Dropdown list having checkboxex in it is defined in usign SLVTable object where rows of the table contains the checkboxes and the name of the checkbox in the row. This object is not directly being identified by the tool. To handle this issue I wrote the following function and register it with SlvTable object to select specific check box:

'desc: it is the parameter which contains name of the checkbox. In my application name is given in second column. Index can be updated based on the application.

Sub ClickSLVTableDropDown(obj, desc)
rows=obj.RowCount
index=0
For cnt=0 to rows-1
var=obj.GetCellData(cnt,1)
If strcomp(var,desc,1)=0 Then
index=cnt
Exit for
End If
Next

set obj=obj.GetCellChildObjects(index,0)
obj(0).click
End Sub

RegisterUserFunc "SlvTable", "ClickSLVTableDropDown", "ClickSLVTableDropDown"




2. Scenario: SlvTable is used to have values in the dropdown but does not have checkboxes. Apart from only the values in the dropdown visible on the screen are counted as number of rows in the SlvTable not all the values it contains. So if you want to loop in the table, the option is not a feasible option.

Organization of the object: Dropdown object is defined as combination of textbox and a button. Clicking on either of these two objects individually the dropdown will come up from which values needs to be selected. Directly entering the value in the text box does not help as the object is organized in such a ways that the value needs to be selected from the table values.

Solution: Click on the SlvTextbox object which would open the drop down. Then use the below code for clicking on the specific value from the dropdown:

Sub SelectValueFromDowpdown(obj, desc)
obj.Click
wait 2
set wsh=createobject("WScript.Shell")
wsh.SendKeys desc
wait 1
wsh.SendKeys "{ENTER}"
End Sub

RegisterUserFunc "SlvEdit", "SelectValueFromDowpdown", "SelectValueFromDowpdown"




3. Scenario:On the page in for navigation to different options for a the navigation options are organized in the way that SlvObject element is embedded in SlvTable object. The scenario may be in such a way that based on certain previous selection the order of appearance of SlvObject may change.

Solution: Get the rows from the SlvTable and find out the order of SlvObject appearance in the table and click the object accordingly.

Sub SelectValue(obj, val)
rows= obj.GetItemsCount
For counter=0 to rows-1
set child=obj.GetItemChildObjects(counter)
child_name=child(0).getRoproperty("text")
If strcomp(child_name,val,1)=0 Then
obj.Select counter
Exit for
End If
Next
End Sub

RegisterUserFunc "SlvList", "SelectValue", "SelectValue"




4. Scenario: Item needs to be selected from SlvComboBox but items are not added to Item property and hence 'Select' method does not work.

Solution: In this scenario loop thru all the elements in the combo box and find out the index of the item you want to select. Then based on this index select the item from the combo box. 

Sub SelectComboOption(obj, combo_option)
total_items=obj.GetItemsCount
For counter=0 to total_items-1
set child= obj.GetItemChildObjects(counter)
item_name=child(0).getRoproperty("text")
If strcomp(item_name,combo_option,1)=0 Then
obj.Select counter
Exit for
End If
Next
End Sub

RegisterUserFunc "SlvComboBox", "SelectComboOption", "SelectComboOption"



5. Scenario: In SLVComboBox select method is able to select the items from the combo box but accordingly changes are not getting reflected. However, while performing similar action manually or using the sendkeys function, the functionality was working. While doing the root cause analysis I found out that in the items collection the items were not added by their name and only class of the item was added in the array.

Solution:  While using object spy I noticed that the dropdown options were being displayed as text block and as child object of the SlvComboBox. Below code shows how the problem is being solved using descriptive programming. A description object is created which represents the item to be selected from the combo box and using childobjects method of the combobox the reference to that object is used to click and select the required item in the combo box.

Browser("").Page("").SlvWindow("").SlvComboBox("").Click
Set  childObj=Description.Create
childObj("classname").value="System.Windows.Controls.TextBlock"
childObj("parent text").value="Filter Name"
set cObj=Browser("").Page("").SlvWindow("").SlvComboBox("").ChildObjects(childObj)
cObj(0).click



Please share if you have alternate ways to resolve these object identification problems. Thanks.

Thursday, November 7, 2013

Quality Center upgrade to v11

I have done an upgrade recently which was from QC 9.2 to ALM11. I am trying to document the steps followed and challenges faced during the upgrade.

Prerequisites for upgrade
  1. QC 9.2 repository should be over file system. 
  2. Note: QC 9.2 repository can be over database or over file system. If it is over database then move those project repositories to over file system as ALM 11 does not support repository over database.
  3. Take a backup of the existing project repository from QC 9.2 server.
  4. Copy the repository backup to the destination server where you are going to install ALM 11.
  5. Take a copy of existing site admin schema and project schema’s from the database server
  6. Restore this backup to the database server which is being used as a DB server for ALM11.
  7. Please note that keep the copy of DB schemas and repository in case of failed upgrade and project gets corrupted. This helps to restore the project again for upgrade.
  8. Make sure entire oracle database is backed up.
  9. Please make sure that all the owners of the project are informed.
  10. HP Service Agreement ID – for downloads, support requests and knowledge documents.
  11. License key for QC 11 installation.
  12. Installation files for ALM 11.

Upgrade Strategy





  •        Backup: Backup of project repository and database schema for site admin and projects.

  •        Install: Installation of QC 11 and upgrading the site admin schema during installation so that all the users  and projects are upgraded with that.

  •        Upgrade and Test: Step by step upgrading the project using the upgrade tool provided by QC 11.

  •        Restore: In case of failed upgrade, restoration of the project using the backup of the project in database and project repository over file system.
Detailed Upgrade Steps
    1. Check if all the project repositories are over file system, if not move those to over file system.
    2. Do a fresh installation of Oracle on the server machine
    3. Take backup of repository from QC 9.2 server
    4. Take backup of database and project schemas
    5. Copy the project repository to the server machine
    6. Create a copy of the site admin schema and project schema in the new database server installed for ALM 11
    7. Start installation of ALM 11 using the existing site admin schema
    8. Upgrade the projects once installation is done by following below steps:
      1. Verify
      2. Repair
      3. Upgrade
    9. Test upgraded production environment
      1. Alert the project stakeholders about the completeness of the project upgrade.
      2. Verify that you can connect to the projects in Site Administration.
      3. Ask project users to check that they can log in to all ALM projects using their account.
      4. Alert project stakeholders to perform the sanity tests.
      5. Ask the users to perform their routine operations and report feedback such as response time or the occurrence of errors.
      6. Ask the users to check new features and functionalities within ALM and provide feedback.
      7. Check the users' group permissions that may be set by default for new features, and modify them if necessary.
      8. Perform a load test on the testing environment to verify that it can handle the intended number of users.
      9. If you are using HP integration or a third-party tool with ALM, validate backward compatibility of the integration.


    Project upgrade to ALM 11



    • Remove the project from the list as this has come as part of site admin schema upgrade and points to the old repository path and database.
    • For “dbid.xml” reference, create an empty project in the fresh ALM installation. Now this dbid.xml file can be used to restore different projects. Location of this file can be anywhere in the system (ex desktop) as while upgrade the actual dbid.xml file of project would be upgraded by the upgrade tool.
    • Update “dbid.xml” file following fields

               <?xml version="1.0" encoding="UTF-8"?>
               <ProjectDescription>
                    <PROJECT_NAME>Project Name</PROJECT_NAME>
                    <DB_TYPE>3</DB_TYPE>
                    <DESCRIPTION>Created on 2013-06-24 20:00:11</DESCRIPTION>
                    <DB_CONNSTR_FORMAT>DB Connection String (will be taken from sample project db file)</DB_CONNSTR_FORMAT>
                    <DB_NATIVE_AUTHENTICATION>N</DB_NATIVE_AUTHENTICATION>
                    <DB_NAME>Name of restored DB schema for the project</DB_NAME>
                    <DBSERVER_NAME>Name of ALM 11 DB server</DBSERVER_NAME>
                    <DB_USER_PASS>Copy password from sample project </DB_USER_PASS>
                    <PR_HAS_VCSDB>N</PR_HAS_VCSDB>
                    <PHYSICAL_DIRECTORY>Location of the project repository in the file system</PHYSICAL_DIRECTORY>
                    <USERS_QUOTA>-1</USERS_QUOTA>
                    <PR_IS_ACTIVE>Y</PR_IS_ACTIVE>
                    <SAQ_IS_ACTIVE>N</SAQ_IS_ACTIVE>
                    <VM_REPOSITORY></VM_REPOSITORY>
                    <PR_LANGUAGE>English</PR_LANGUAGE>
                    <PROJECT_TYPE>Standard</PROJECT_TYPE>
                    <IS_TEMPLATE>N</IS_TEMPLATE>
                    <PROJECT_UID>Take this from sample project dbid and change some letters</PROJECT_UID>
                    <PR_SMART_REPOSITORY_ENABLED>Make this parameter as ‘N’</PR_SMART_REPOSITORY_ENABLED>
                    <PR_IS_QPM_AUTO_CALC_ENABLED>Y</PR_IS_QPM_AUTO_CALC_ENABLED>
              </ProjectDescription>

    • After updating dbid.xml file, we need to restore the project using this dbid file. For this we have two option:
      •  To go to the domain you want to restore the project into, right click and select “Restore Project” option
      •  Or on the right hand pane on the select restore project option
    • Select the updated bdid.xml file to restore the project.
    • Click on restore button.
    • Deactivate the project to perform further upgrade activities.
    • In the right hand pane check if DB path and repository path are correct.
    • Select “Verify Project” option and start verification of the project
      • Logs are stored at: \sa\DomsInfo\MaintenanceData\out\\\
    • Repair the project by selecting the “Repair Project” option
    • Upgrade the project by selecting “Upgrade Project” option
    • Active the project using “Activate Project” option
    • Test the project by logging into ALM and check the project if it is accessible.

    Upgrade Issues and Workarounds

    Issue 1: APM_2.0, BPTA_2.00 and SPI_2.0 were not available while upgrade.

    Resolution: The folders were taken from QC9.2 installation folder and placed at the ALM11 server. This helped to solve the issue. These extension files contain additional fields which need to be created as part of enabling these extensions in ALM.


    Issue 2: Upgrade extension files were missing from APM_2.0_to_2.6 and SPI_2.0_to_2.6

    Resolution: There were other upgrade extension files were available in the folder for other version upgrades ex. 2.1 to 2.6, 2.5 to 2.6 etc. This is an xml file which calls a java file which is invoked for version upgrade and this is the same file called for all the different enterprise extension versions which needs to be upgraded. To fix this issue we copied an existing xml file and renamed it to “upgrade_APM_2.0_to_2.6” and “upgrade_SPI_2.0_to_2.6” respectively.


    Issue 3: While upgrade we got the error unable to load the file, fields mentioned in the xml file are already available in the main database.

    Resolution: What we understood from this issue is that the given XML file is used to create additional fields in the database to enable the extension. If the fields were already available upgrade tool was throwing the error. To fix this issue we deleted the fields which were there in the database. Since it is the file referring to BPTA extension and BPT module is integrated with ALM11 as a default module, all the fields were available in the database. So we deleted all the fields from the xml file.


    Issue 4: Post upgrade while clicking on Test script tab for the existing script ALM was throwing the error:
    When we right click on the component it shows “go to flow instead of “Go to component”. So components are being shown as a flow after upgrade.

    Resolution: We created on sample scenario and when we are clicking on the newly created component it is showing that as go to component and this functionality is working fine.
    When we compared closely the their corrosponding rown in the database we found out that value of “CO_BPTA_FLOW_TEST_ID” was set to 0 which means that it is pointing to a flow with ID=0. But in reality it was a business component and hence this field should be NULL. As a resolution we changed the value to this field to null and this solved the issue. Since we upgraded from QC 9.2 and there are no flows used in that version there was not possibility of anything malfunctioning by this change.
    Query used: UPDATE COMPONENT set CO_BPTA_FLOW_TEST_ID=''

    Lessons Learnt

    1. Setup files for installation should be downloaded beforehand
    2. Availability of licenses should be confirmed
    3. Availability of HP-SAID should be there to ensure access to HP support documentation and patches
    4. Should keep backup in case of project gets corrupted and needs to be restored for upgrade
    Above write-up is written based on my experience with the upgrade. It would be great if you can add your experiences to this :-))

    References:
    1. ALM 11 Admin guide
    2. ALM 11 upgrade best practices
    3. Various blog posts
    4. Discussion with learned colleagues

    Saturday, October 19, 2013

    Automating SAP portal based application automation issues and possible resolutions

    Few challenges which I have encountered while automating the SAP portal based applications and I am trying to put them below hoping it would help someone like me (or probably me in future looking for help :-) about code) :

    Before going to the specific challenges I would like to write about what I have understood about the SAP NetWeaver portal. They have tried to standardize the controls by clubbing basic HTML web elements in a specific order and then come up with a framework for the portal. So basically at the core it is web add in which helps to identify the objects on the application. Since the portal is relatively new (compared to ECC), it is not as stable as ECC is when it comes to automating the UI using QTP. In this context few of the challenges which I am trying to document below:

    1. SAPCombo: It is recorded by the tool but when you try to play back the script the control is not identified by the tool. How it actually works is that when you spy the object you will find that it is a WebEdit and on its click event a webtable opens up which contains the elements of the combo box. To handle this there may be two ways:

    a. First add the WebEdit to your object repository-> click on WebEdit in the code-> create a description of the web element from the table you want to click-> add web table to the repository-> use child item method of the WebTable to click on the element you want to click

    objDesc=Description.create
    objDesc("micClass").value="WebElement"
    objDesc("name").value="One"

    Browser("Browser").Page("Page").WebEdit("EditBox").click
    set childobj=Browser("Browser").Page("Page").WebTable("Table").childobjects(objDesc)
    childobj(0).click  (Just find out which native method this object support and invoke that method)

    b. Create a Wscript.Shell object and use the sendkey method to perform the action. There may be issues while using this method and I prefer the above method, but in some case when you are not able to figure out the unique identification properties or the placement of the WebEdit(required for childitem method) object in the table cell, it is the method left to go with and works well.

    2. SAPEdit: I have noticed that qtp tries to identify the SapEdit using html id property to identify the object, but every time you login a new id is assigned to the object and the script fails. I try to solve this problem is by going by regular expression and index property. Mostly the id is represented something like: WD78DA, where initial WD is constant and remaining string keeps changing. To address this issue what I do is put it as a regular expression- WD.*, and then find out the index of the edit box on the screen and use this property. I have not see this index changing and it helps me to solve this issue. Don't forget to remove the "sapattachedtext" property if that is being used by the tool

    3. SAPEdit inside a WebTable: In such case apart from going by indexes and regular expression, you can use childitem method to enter the value in a particular SAPEdit box embedded inside the WebTable.

    Browser().Page().WebTable().childItem(1,2, "WebEdit",0).set "Value"

    4.SAPButton: Sometimes I have experienced that when you are adding and SAPButton using add button in the object repository, you will find two SAPButton objects at the bottom in the object hierarchy which QTP is showing, in that case you just go ahead and check the second last object in the hierarchy as I have experienced that it had the name property which helped tool to identify the object while execution whereas tool faced difficulty when I added the last object in the hierarchy as it did not have fixed recognition properties associated with the object.

    Please post other issues as well if you have faced while automating SAP netweaver portal and how you resolved those. That would help. Thanks.

    Tuesday, October 8, 2013

    Some Custom functions in QTP

    I am placing few of the custom function which I have written, below. These can be reused in the same form without changes and will try to add more here.

    '***************************************************************************
    Function current_date(date_format)
    dim c_date
       Select Case date_format
      Case "dd.mm.yyyy"
    c_date=Day(now())&"."&Month(Now())&"."&Year(Now())

    Case "dd/mm/yyyy"
    c_date=Day(now())&"/"&Month(Now())&"/"&Year(Now())
       End Select

       current_date=c_date
    End Function

    '***************************************************************************
    Sub sap_exit()
    SAPGuiSession("type:=GuiSession","name:=ses.0.").Reset " "
    SAPGuiSession("type:=GuiSession","name:=ses.0.").Reset "ex"
    End Sub

    '***************************************************************************
    'This function returns arrays of dates from_date to to_date
    Function middle_dates(from_date, to_date)
    f_date=getdate(from_date)
    t_date=getdate(to_date)

    'msgbox "start:"&f_date &"/n End:" & t_date
    diff=datediff("d",f_date,t_date)
    num_elements=CInt(diff)
    dim arr_date()
    ReDim arr_date(num_elements)
    arr_date(0)=format_date(f_date,"dd.mm.yyyy")
    For counter=1 to diff
    d_var=dateadd("d", counter,f_date)
    arr_date(counter)=format_date(d_var, "dd.mm.yyyy")
    Next
    middle_dates=arr_date
    End Function
    '***************************************************************************
    ' This function takes date in dd.mm.yyyy format and convert it to the system date format to be processed
    function getdate(d)
    str=Split(d,".")
    str_day=str(0)
    str_month=str(1)
    str_year=str(2)
    d=dateserial(str_year,str_month,str_day)
    'msgbox d
    getdate=d
    end function
    '***************************************************************************
    'This cuntion converts date to the required format
    Function format_date(dt, format)
    return_date=""
      d=CInt(day(dt))
    If d<10 Then
    d="0"&cstr(d)
    End If

    m=cint(month(dt))
    If m<10 Then
    m="0" & cstr(m)
    End If

    y=year(dt)

    Select Case (format)
    Case "dd.mm.yyyy"
     return_date=d &"." & m & "." & y 

    End Select
    format_date=return_date
    End Function
    '***************************************************************************
    'get temp file path
    Function getTempFilePath(fileName)
    Set objShell = CreateObject("WScript.Shell")
    Set colEnvironment = objShell.Environment("PROCESS")
    objPath = colEnvironment("temp")
    getTempFilePath=objPath&"\"&fileName
    End Function
    '***************************************************************************
    'Generic function to capture screenshot
    'Funciton to capture the screenshot
    Function CaptureScreenshot(obj)
    obj.capturebitmap gettempfilepath("screenshot.png"), true
        CaptureScreenshot=gettempfilepath("screenshot.png")
    End Function

    '***************************************************************************
    'Function Name:current_time
    'Function returns current time in HH:MM:SS format
    '******************************************************************************************************************
    Function current_time()
       current_time=hour(now())&":"&minute(now())&":"&second(now())
    End Function
    '***************************************************************************
    'This subroutine logs SAP status bar event
    Sub ReportStatusBarEvent_Desc(obj,desc, screenshot)
       text=obj.getROProperty("text")
       msgtype=obj.getROProperty("messagetype")
    If Strcomp(msgtype,"S",1)=0 Then
    reporter.ReportEvent  micPass,desc, text, screenshot
    else
    reporter.ReportEvent  micFail,desc, text, screenshot
    End If
    End Sub


    '******************************************************************************************************************
    'Function Name:CustomWait
    'Description:This function is written to have custom wait on object
    '******************************************************************************************************************
    Sub CustomWait(obj, MaxWaitTime)
    flag=true
    counter=0
    While (flag AND counter<=MaxWaitTime)
    If obj.exist Then
    'If obj.getroproperty("enabled") Then
    flag=false
    wait 3
    'End If
    else
    wait 3
    counter=counter+3
    End If
    Wend
    End Sub

    '*****************************************************************************************************************
    'Function Name:CustomSendKeys
    'Description:Function to use sendkeys operation
    '******************************************************************************************************************
    Sub CustomSendKeys(obj, desc)
        set wsh=createobject("WScript.Shell")
    wsh.SendKeys desc
    Set wsh=nothing
    End Sub
    '******************************************************************************************************************

    'You can register these functions with multiple object using registeruserfunc .