Testing Tips"

From Documentation
m
 
(14 intermediate revisions by one other user not shown)
Line 1: Line 1:
 
{{ZKDevelopersReferencePageHeader}}
 
{{ZKDevelopersReferencePageHeader}}
  
=ID and UUID=
+
Here we introduce some tips when you use a browser testing tool to test ZK-based applications, e.g. [https://jmeter.apache.org/ JMeter], [https://testcafe.io/ TestCafe], selenium.
  
By default, the desktop's ID and component's UUID are randomized for preventing Cross-Site Request Forgery (CSRF) and allowing multiple desktops coexists in the same web page (such as Portlet). However, it also means the DOM element's IDs will change from one test run to another, since component's UUID will become DOM element's ID at the browser, .
+
=Deal with Randomized UUID=
  
If your test code runs at the server (such [[ZK_Developer's_Reference/Testing/ZATS|ZATS]] and JUnit), it is not an issue at all (since DOM elements are available at the client only). However, if your test tool runs at the browser, you have to resolve it with one of the following solutions:
+
By default, a desktop's ID and a component's UUID are randomized for preventing Cross-Site Request Forgery (CSRF) and allowing multiple desktops to coexist on the same web page (such as Portlet). A component's UUID is auto-generated by ZK and different from its ID. The UUID is used as a component DOM elements' id in a browser. However, it also means the DOM element's IDs will change from one test run to another.
  
# Not to depend on DOM element's ID. Rather, use component's ID and/or component's parent-child-sibling relationship.
+
If your test code runs at the server (such [[ZK_Developer's_Reference/Testing/ZATS|ZATS]] and JUnit), it is not an issue at all (since DOM elements are available at the client only). However, if your test tool runs in a browser, you have to locate an element with one of the following approaches:
 +
 
 +
# Not to depend on a DOM element's ID. Rather, use a component's ID and/or component's parent-child-sibling relationship.
 
# Implement <javadoc type="interface">org.zkoss.zk.ui.sys.IdGenerator</javadoc> to generate UUID in a predictable and repeatable way
 
# Implement <javadoc type="interface">org.zkoss.zk.ui.sys.IdGenerator</javadoc> to generate UUID in a predictable and repeatable way
  
==Approach 1: Use Widget's ID==
+
Let me explain them in detail.
With [[ZK Developer's Reference/Overture/Architecture Overview|Server+client architecture]], ZK maintains an ''identical'' world at the client. If your test tool is able to access JavaScript at the client, your test code could depend on the widget's ID and widget's parent-child-relationship as your application code depends on the component's ID and component's parent-child-relationship. They are ''identical'', except one is JavaScript and called <javadoc directory="jsdoc">zk.Widget</javadoc>, while the other is Java and called <javadoc type="interface">org.zkoss.zk.ui.Component</javadoc>.
+
 
 +
==Approach 1: Locate by a component's ID==
 +
With [[ZK Developer's Reference/Overture/Architecture Overview|Server+client architecture]], ZK maintains an ''identical'' world at the client. If your test tool is able to access JavaScript at the client, your test code can depend on a component's ID and its widget's parent-child relationship as your application code depends on the component's ID and component's parent-child relationship. They are ''identical'', except one is JavaScript and called <javadoc directory="jsdoc">zk.Widget</javadoc>, while the other is Java and called <javadoc type="interface">org.zkoss.zk.ui.Component</javadoc>.
 +
 
 +
This is a suggested approach since it is much easier to test an application at the same abstract level -- the component level, aka., the widget level (rather than the DOM level).
 +
 
 +
To retrieve widgets at the client, you can use one of the following JavaScript API:
  
This is a suggested approach, since it is much easier to test an application in the same abstract level -- the component level, aka., the widget level (rather than DOM level).
+
* <javadoc directory="jsdoc">_global_.jq</javadoc>
 +
* <javadoc directory="jsdoc" method="$(zk.Object, _global_.Map)">zk.Widget</javadoc>
  
To retrieve widgets at the client, you could use <javadoc directory="jsdoc">_global_.jq</javadoc> and/or <javadoc directory="jsdoc" method="$(zk.Object, _global_.Map)">zk.Widget</javadoc> (they are all client-side API). <javadoc directory="jsdoc">_global_.jq</javadoc> allows your test code to access the components directly, so the test code could depend on widget's ID (<javadoc directory="jsdoc" method="id">zk.Widget</javadoc>) and the widget tree (<javadoc directory="jsdoc" method="firstChild">zk.Widget</javadoc>, <javadoc directory="jsdoc" method="nextSibling">zk.Widget</javadoc> and so on).
+
<javadoc directory="jsdoc">_global_.jq</javadoc> allows your test code to access the components directly, so the test code could depend on a component's ID (<javadoc directory="jsdoc" method="id">zk.Widget</javadoc>) and the widget tree (<javadoc directory="jsdoc" method="firstChild">zk.Widget</javadoc>, <javadoc directory="jsdoc" method="nextSibling">zk.Widget</javadoc> and so on).
  
 
<source lang="javascript">
 
<source lang="javascript">
 
jq('@window[border="normal"]') //returns a list of window whose border is normal
 
jq('@window[border="normal"]') //returns a list of window whose border is normal
jq('$x'); //returns the widget whose ID is x
+
jq('$x'); //returns the widget whose component ID is x, <div id="x"/>
 
jq('$x $y'); //returns the widget whose ID is y and it is in an ID space owned by x
 
jq('$x $y'); //returns the widget whose ID is y and it is in an ID space owned by x
 
</source>
 
</source>
  
With this approach, you still can verify the DOM structure if you want, since it can be retrieved from widget's <javadoc directory="jsdoc" method="$n()">zk.Widget</javadoc>.
+
With this approach, you still can verify the DOM structure if you want, since it can be retrieved from a widget's <javadoc directory="jsdoc" method="$n()">zk.Widget</javadoc>.
  
 
[http://code.google.com/p/zk-ztl/ ZTL] is a typical example that takes this approach. For more information, please refer to the [[ZK_Developer's_Reference/Testing/ZTL|ZTL section]].
 
[http://code.google.com/p/zk-ztl/ ZTL] is a typical example that takes this approach. For more information, please refer to the [[ZK_Developer's_Reference/Testing/ZTL|ZTL section]].
  
==Approach 2: Implement ID Generator==
+
==Approach 2: Use ID Generator==
If your test tool running at the client cannot access JavaScript, you could implement an ID generator to generate desktop's ID and component's UUID in a predictable and repeatable matter.
+
If your testing tool running in a browser cannot access JavaScript, you can implement an ID generator to generate a desktop's ID and component's UUID in a predictable and repeatable manner.
  
To implement a custom ID generator, you have to do the following:
+
{{versionSince| 7.0.0}}
  
* Implement a Java class that implements <javadoc type="interface">org.zkoss.zk.ui.sys.IdGenerator</javadoc>.
+
Since ZK 7.0.0, ZK provides a static ID generator implementation for testing, to use <javadoc type="class">org.zkoss.zk.ui.impl.StaticIdGenerator</javadoc>, simply add it to zk.xml.
* Specify the Java class in WEB-INF/zk.xml with the [[ZK Configuration Reference/zk.xml/The system-config Element|id-generator-class]] element. For example,
 
  
 
<source lang="xml">
 
<source lang="xml">
 
<system-config>
 
<system-config>
     <id-generator-class>my.IdGenerator</id-generator-class>
+
     <id-generator-class>org.zkoss.zk.ui.impl.StaticIdGenerator</id-generator-class>
 
</system-config>
 
</system-config>
 
</source>
 
</source>
  
{{versionSince| 7.0.0}}
+
To implement a custom ID generator, you have to do the following:
  
Since ZK 7.0.0, we provide a static ID generator implementation for testing, to use <javadoc type="class">org.zkoss.zk.ui.impl.StaticIdGenerator</javadoc>, simply add it to zk.xml.
+
* Implement a Java class that implements <javadoc type="interface">org.zkoss.zk.ui.sys.IdGenerator</javadoc>.
 +
* Specify the Java class FQCN at [[ZK Configuration Reference/zk.xml/The system-config Element|id-generator-class]] element in <code>zk.xml</code>. For example,
  
 
<source lang="xml">
 
<source lang="xml">
 
<system-config>
 
<system-config>
     <id-generator-class>org.zkoss.zk.ui.impl.StaticIdGenerator</id-generator-class>
+
     <id-generator-class>my.IdGenerator</id-generator-class>
 
</system-config>
 
</system-config>
 
</source>
 
</source>
 +
 +
=== Examples ===
 +
* [https://github.com/zkoss/zkbooks/blob/master/developersreference/developersreference/src/main/java/org/zkoss/reference/developer/testing/ComponentIdFirstGenerator.java ComponentIdFirstGenerator]. Unlike [https://www.zkoss.org/javadoc/latest/zk/org/zkoss/zk/ui/impl/StaticIdGenerator.html StaticIdGenerator], component creation order doesn't affect id generation.
 +
* [https://github.com/zkoss/zkbooks/blob/master/developersreference/developersreference/src/main/java/org/zkoss/reference/developer/testing/StaticIdGeneratorExt.java StaticIdGeneratorExt]. It generates desktop id in a predictable way.
  
 
=Different Configuration for Different Environment=
 
=Different Configuration for Different Environment=
If you prefer to have a different configuration for the testing environment (such as specifying ID generator for testing), you could put the configuration in a separated file, say, <tt>WEB-INF/config/zk-testing.xml</tt> with the following content.
+
If you prefer to have a different configuration for the testing environment (such as specifying ID generator for testing), you could put the configuration in a separate file, say, <code>WEB-INF/config/zk-testing.xml</code> with the following content.
  
 
<source lang="xml">
 
<source lang="xml">
Line 67: Line 80:
 
=Disabled UUID recycle=
 
=Disabled UUID recycle=
  
If you want to generate uuid with some conditional , you might also want to disable UUID recycle. ( It will reuse all the uuids from removed components.)  
+
If you want to generate UUID with some conditions, you might also want to disable UUID recycling. ( It will reuse all the UUIDs from removed components.)  
You could set the properties [[ZK_Configuration_Reference/zk.xml/The_Library_Properties/org.zkoss.zk.ui.uuidRecycle.disabled|org.zkoss.zk.ui.uuidRecycle.disabled]] in zk.xml .
+
You could set the properties [[ZK_Configuration_Reference/zk.xml/The_Library_Properties/org.zkoss.zk.ui.uuidRecycle.disabled|org.zkoss.zk.ui.uuidRecycle.disabled]] in zk.xml.
 +
 
  
  
=Version History=
 
{{LastUpdated}}
 
{| border='1px' | width="100%"
 
! Version !! Date !! Content
 
|-
 
| &nbsp;
 
| &nbsp;
 
| &nbsp;
 
|}
 
  
 
{{ZKDevelopersReferencePageFooter}}
 
{{ZKDevelopersReferencePageFooter}}

Latest revision as of 08:34, 2 February 2024

Here we introduce some tips when you use a browser testing tool to test ZK-based applications, e.g. JMeter, TestCafe, selenium.

Deal with Randomized UUID

By default, a desktop's ID and a component's UUID are randomized for preventing Cross-Site Request Forgery (CSRF) and allowing multiple desktops to coexist on the same web page (such as Portlet). A component's UUID is auto-generated by ZK and different from its ID. The UUID is used as a component DOM elements' id in a browser. However, it also means the DOM element's IDs will change from one test run to another.

If your test code runs at the server (such ZATS and JUnit), it is not an issue at all (since DOM elements are available at the client only). However, if your test tool runs in a browser, you have to locate an element with one of the following approaches:

  1. Not to depend on a DOM element's ID. Rather, use a component's ID and/or component's parent-child-sibling relationship.
  2. Implement IdGenerator to generate UUID in a predictable and repeatable way

Let me explain them in detail.

Approach 1: Locate by a component's ID

With Server+client architecture, ZK maintains an identical world at the client. If your test tool is able to access JavaScript at the client, your test code can depend on a component's ID and its widget's parent-child relationship as your application code depends on the component's ID and component's parent-child relationship. They are identical, except one is JavaScript and called Widget, while the other is Java and called Component.

This is a suggested approach since it is much easier to test an application at the same abstract level -- the component level, aka., the widget level (rather than the DOM level).

To retrieve widgets at the client, you can use one of the following JavaScript API:

jq allows your test code to access the components directly, so the test code could depend on a component's ID (Widget.id) and the widget tree (Widget.firstChild, Widget.nextSibling and so on).

jq('@window[border="normal"]') //returns a list of window whose border is normal
jq('$x'); //returns the widget whose component ID is x, <div id="x"/>
jq('$x $y'); //returns the widget whose ID is y and it is in an ID space owned by x

With this approach, you still can verify the DOM structure if you want, since it can be retrieved from a widget's Widget.$n().

ZTL is a typical example that takes this approach. For more information, please refer to the ZTL section.

Approach 2: Use ID Generator

If your testing tool running in a browser cannot access JavaScript, you can implement an ID generator to generate a desktop's ID and component's UUID in a predictable and repeatable manner.

Since 7.0.0

Since ZK 7.0.0, ZK provides a static ID generator implementation for testing, to use StaticIdGenerator, simply add it to zk.xml.

<system-config>
    <id-generator-class>org.zkoss.zk.ui.impl.StaticIdGenerator</id-generator-class>
</system-config>

To implement a custom ID generator, you have to do the following:

<system-config>
    <id-generator-class>my.IdGenerator</id-generator-class>
</system-config>

Examples

Different Configuration for Different Environment

If you prefer to have a different configuration for the testing environment (such as specifying ID generator for testing), you could put the configuration in a separate file, say, WEB-INF/config/zk-testing.xml with the following content.

<zk>
  <system-config>
    <id-generator-class>my.IdGenerator</id-generator-class>
  </system-config>
</zk>

Then, you could you could specify -Dorg.zkoss.zk.config.path=/WEB-INF/config/zk-testing.xml as one of the arguments when starting the Web server.


Disabled UUID recycle

If you want to generate UUID with some conditions, you might also want to disable UUID recycling. ( It will reuse all the UUIDs from removed components.) You could set the properties org.zkoss.zk.ui.uuidRecycle.disabled in zk.xml.




Last Update : 2024/02/02

Copyright © Potix Corporation. This article is licensed under GNU Free Documentation License.