RobotFramework之XML

2019独角兽企业重金招聘Python工程师标准>>> hot3.png

XML

Library version: 3.0.4
Library scope: global
Named arguments: supported

Introduction

Robot Framework test library for verifying and modifying XML documents.

As the name implies, XML is a test library for verifying contents of XML files. In practice it is a pretty thin wrapper on top of Python's ElementTree XML API.

The library has the following main usages:

  • Parsing an XML file, or a string containing XML, into an XML element structure and finding certain elements from it for for further analysis (e.g. Parse XML and Get Element keywords).
  • Getting text or attributes of elements (e.g. Get Element Text and Get Element Attribute).
  • Directly verifying text, attributes, or whole elements (e.g Element Text Should Be and Elements Should Be Equal).
  • Modifying XML and saving it (e.g. Set Element Text, Add Element and Save XML).

Table of contents

  • Parsing XML
  • Using lxml
  • Example
  • Finding elements with xpath
  • Element attributes
  • Handling XML namespaces
  • Boolean arguments
  • Shortcuts
  • Keywords

Parsing XML

XML can be parsed into an element structure using Parse XML keyword. It accepts both paths to XML files and strings that contain XML. The keyword returns the root element of the structure, which then contains other elements as its children and their children. Possible comments and processing instructions in the source XML are removed.

XML is not validated during parsing even if has a schema defined. How possible doctype elements are handled otherwise depends on the used XML module and on the platform. The standard ElementTree strips doctypes altogether but when using lxml they are preserved when XML is saved. With IronPython parsing XML with a doctype is not supported at all.

The element structure returned by Parse XML, as well as elements returned by keywords such as Get Element, can be used as the source argument with other keywords. In addition to an already parsed XML structure, other keywords also accept paths to XML files and strings containing XML similarly as Parse XML. Notice that keywords that modify XML do not write those changes back to disk even if the source would be given as a path to a file. Changes must always saved explicitly using Save XML keyword.

When the source is given as a path to a file, the forward slash character (/) can be used as the path separator regardless the operating system. On Windows also the backslash works, but it the test data it needs to be escaped by doubling it (\\). Using the built-in variable ${/} naturally works too.

Using lxml

By default this library uses Python's standard ElementTree module for parsing XML, but it can be configured to use lxml module instead when importing the library. The resulting element structure has same API regardless which module is used for parsing.

The main benefits of using lxml is that it supports richer xpath syntax than the standard ElementTree and enables using Evaluate Xpath keyword. It also preserves the doctype and possible namespace prefixes saving XML.

The lxml support is new in Robot Framework 2.8.5.

Example

The following simple example demonstrates parsing XML and verifying its contents both using keywords in this library and in BuiltIn and Collections libraries. How to use xpath expressions to find elements and what attributes the returned elements contain are discussed, with more examples, in Finding elements with xpath and Element attributes sections.

In this example, as well as in many other examples in this documentation, ${XML} refers to the following example XML document. In practice ${XML} could either be a path to an XML file or it could contain the XML itself.


  text
  
    
  
  
    more text
    
    
  
  
    

Text with bold and italics.

${root} = Parse XML ${XML}    
Should Be Equal ${root.tag} example    
${first} = Get Element ${root} first  
Should Be Equal ${first.text} text    
Dictionary Should Contain Key ${first.attrib} id    
Element Text Should Be ${first} text    
Element Attribute Should Be ${first} id 1  
Element Attribute Should Be ${root} id 1 xpath=first
Element Attribute Should Be ${XML} id 1 xpath=first

Notice that in the example three last lines are equivalent. Which one to use in practice depends on which other elements you need to get or verify. If you only need to do one verification, using the last line alone would suffice. If more verifications are needed, parsing the XML with Parse XML only once would be more efficient.

Finding elements with xpath

ElementTree, and thus also this library, supports finding elements using xpath expressions. ElementTree does not, however, support the full xpath syntax, and what is supported depends on its version. ElementTree 1.3 that is distributed with Python 2.7 supports richer syntax than earlier versions.

The supported xpath syntax is explained below and ElementTree documentation provides more details. In the examples ${XML} refers to the same XML structure as in the earlier example.

If lxml support is enabled when importing the library, the whole xpath 1.0 standard is supported. That includes everything listed below but also lot of other useful constructs.

Tag names

When just a single tag name is used, xpath matches all direct child elements that have that tag name.

${elem} = Get Element ${XML} third
Should Be Equal ${elem.tag} third  
@{children} = Get Elements ${elem} child
Length Should Be ${children} 2  

Paths

Paths are created by combining tag names with a forward slash (/). For example, parent/child matches all child elements under parent element. Notice that if there are multiple parent elements that all have childelements, parent/child xpath will match all these child elements.

${elem} = Get Element ${XML} second/child
Should Be Equal ${elem.tag} child  
${elem} = Get Element ${XML} third/child/grandchild
Should Be Equal ${elem.tag} grandchild  

Wildcards

An asterisk (*) can be used in paths instead of a tag name to denote any element.

@{children} = Get Elements ${XML} */child
Length Should Be ${children} 3  

Current element

The current element is denoted with a dot (.). Normally the current element is implicit and does not need to be included in the xpath.

Parent element

The parent element of another element is denoted with two dots (..). Notice that it is not possible to refer to the parent of the current element. This syntax is supported only in ElementTree 1.3 (i.e. Python/Jython 2.7 and newer).

${elem} = Get Element ${XML} */second/..
Should Be Equal ${elem.tag} third  

Search all sub elements

Two forward slashes (//) mean that all sub elements, not only the direct children, are searched. If the search is started from the current element, an explicit dot is required.

@{elements} = Get Elements ${XML} .//second
Length Should Be ${elements} 2  
${b} = Get Element ${XML} html//b
Should Be Equal ${b.text} bold  

Predicates

Predicates allow selecting elements using also other criteria than tag names, for example, attributes or position. They are specified after the normal tag name or path using syntax path[predicate]. The path can have wildcards and other special syntax explained above.

What predicates ElementTree supports is explained in the table below. Notice that predicates in general are supported only in ElementTree 1.3 (i.e. Python/Jython 2.7 and newer).

Predicate Matches Example
@attrib Elements with attribute attrib. second[@id]
@attrib="value" Elements with attribute attrib having value value. *[@id="2"]
position Elements at the specified position. Position can be an integer (starting from 1), expression last(), or relative expression like last() - 1. third/child[1]
tag Elements with a child element named tag. third/child[grandchild]

Predicates can also be stacked like path[predicate1][predicate2]. A limitation is that possible position predicate must always be first.

Element attributes

All keywords returning elements, such as Parse XML, and Get Element, return ElementTree's Element objects. These elements can be used as inputs for other keywords, but they also contain several useful attributes that can be accessed directly using the extended variable syntax.

The attributes that are both useful and convenient to use in the test data are explained below. Also other attributes, including methods, can be accessed, but that is typically better to do in custom libraries than directly in the test data.

The examples use the same ${XML} structure as the earlier examples.

tag

The tag of the element.

${root} = Parse XML ${XML}
Should Be Equal ${root.tag} example

text

The text that the element contains or Python None if the element has no text. Notice that the text does not contain texts of possible child elements nor text after or between children. Notice also that in XML whitespace is significant, so the text contains also possible indentation and newlines. To get also text of the possible children, optionally whitespace normalized, use Get Element Text keyword.

${1st} = Get Element ${XML} first
Should Be Equal ${1st.text} text  
${2nd} = Get Element ${XML} second/child
Should Be Equal ${2nd.text} ${NONE}  
${p} = Get Element ${XML} html/p
Should Be Equal ${p.text} \n${SPACE*6}Text with${SPACE}  

tail

The text after the element before the next opening or closing tag. Python None if the element has no tail. Similarly as with text, also tail contains possible indentation and newlines.

${b} = Get Element ${XML} html/p/b
Should Be Equal ${b.tail} ${SPACE}and${SPACE}  

attrib

A Python dictionary containing attributes of the element.

${2nd} = Get Element ${XML} second
Should Be Equal ${2nd.attrib['id']} 2  
${3rd} = Get Element ${XML} third
Should Be Empty ${3rd.attrib}    

Handling XML namespaces

ElementTree and lxml handle possible namespaces in XML documents by adding the namespace URI to tag names in so called Clark Notation. That is inconvenient especially with xpaths, and by default this library strips those namespaces away and moves them to xmlns attribute instead. That can be avoided by passing keep_clark_notation argument to Parse XML keyword. Alternatively Parse XML supports stripping namespace information altogether by using strip_namespaces argument. The pros and cons of different approaches are discussed in more detail below.

How ElementTree handles namespaces

If an XML document has namespaces, ElementTree adds namespace information to tag names in Clark Notation (e.g. {http://ns.uri}tag) and removes original xmlns attributes. This is done both with default namespaces and with namespaces with a prefix. How it works in practice is illustrated by the following example, where ${NS} variable contains this XML document:


  
    
  
${root} = Parse XML ${NS} keep_clark_notation=yes
Should Be Equal ${root.tag} {http://www.w3.org/1999/XSL/Transform}stylesheet  
Element Should Exist ${root} {http://www.w3.org/1999/XSL/Transform}template/{http://www.w3.org/1999/xhtml}html  
Should Be Empty ${root.attrib}    

As you can see, including the namespace URI in tag names makes xpaths really long and complex.

If you save the XML, ElementTree moves namespace information back to xmlns attributes. Unfortunately it does not restore the original prefixes:


  
    
  

The resulting output is semantically same as the original, but mangling prefixes like this may still not be desirable. Notice also that the actual output depends slightly on ElementTree version.

Default namespace handling

Because the way ElementTree handles namespaces makes xpaths so complicated, this library, by default, strips namespaces from tag names and moves that information back to xmlns attributes. How this works in practice is shown by the example below, where ${NS} variable contains the same XML document as in the previous example.

${root} = Parse XML ${NS}    
Should Be Equal ${root.tag} stylesheet    
Element Should Exist ${root} template/html    
Element Attribute Should Be ${root} xmlns http://www.w3.org/1999/XSL/Transform  
Element Attribute Should Be ${root} xmlns http://www.w3.org/1999/xhtml xpath=template/html

Now that tags do not contain namespace information, xpaths are simple again.

A minor limitation of this approach is that namespace prefixes are lost. As a result the saved output is not exactly same as the original one in this case either:


  

Also this output is semantically same as the original. If the original XML had only default namespaces, the output would also look identical.

Namespaces when using lxml

This library handles namespaces same way both when using lxml and when not using it. There are, however, differences how lxml internally handles namespaces compared to the standard ElementTree. The main difference is that lxml stores information about namespace prefixes and they are thus preserved if XML is saved. Another visible difference is that lxml includes namespace information in child elements got with Get Element if the parent element has namespaces.

Stripping namespaces altogether

Because namespaces often add unnecessary complexity, Parse XML supports stripping them altogether by using strip_namespaces=True. When this option is enabled, namespaces are not shown anywhere nor are they included if XML is saved.

Attribute namespaces

Attributes in XML documents are, by default, in the same namespaces as the element they belong to. It is possible to use different namespaces by using prefixes, but this is pretty rare.

If an attribute has a namespace prefix, ElementTree will replace it with Clark Notation the same way it handles elements. Because stripping namespaces from attributes could cause attribute conflicts, this library does not handle attribute namespaces at all. Thus the following example works the same way regardless how namespaces are handled.

${root} = Parse XML  
Element Attribute Should Be ${root} id 1
Element Attribute Should Be ${root} {http://my.ns}id 2

Boolean arguments

Some keywords accept arguments that are handled as Boolean values true or false. If such an argument is given as a string, it is considered false if it is either an empty string or case-insensitively equal to falsenoneor no. Other strings are considered true regardless their value, and other argument types are tested using the same rules as in Python.

True examples:

Parse XML ${XML} keep_clark_notation=True # Strings are generally true.
Parse XML ${XML} keep_clark_notation=yes # Same as the above.
Parse XML ${XML} keep_clark_notation=${TRUE} # Python True is true.
Parse XML ${XML} keep_clark_notation=${42} # Numbers other than 0 are true.

False examples:

Parse XML ${XML} keep_clark_notation=False # String false is false.
Parse XML ${XML} keep_clark_notation=no # Also string no is false.
Parse XML ${XML} keep_clark_notation=${EMPTY} # Empty string is false.
Parse XML ${XML} keep_clark_notation=${FALSE} # Python False is false.

Prior to Robot Framework 2.9, all non-empty strings, including false and no, were considered to be true. Considering none false is new in Robot Framework 3.0.3.

Importing

Arguments Documentation
use_lxml=False

Import library with optionally lxml mode enabled.

By default this library uses Python's standard ElementTree module for parsing XML. If use_lxml argument is given a true value (see Boolean arguments), the library will use lxml module instead. See Using lxml section for benefits provided by lxml.

Using lxml requires that the lxml module is installed on the system. If lxml mode is enabled but the module is not installed, this library will emit a warning and revert back to using the standard ElementTree.

The support for lxml is new in Robot Framework 2.8.5.

Shortcuts

Add Element · Clear Element · Copy Element · Element Attribute Should Be · Element Attribute Should Match · Element Should Exist · Element Should Not Exist · Element Should Not Have Attribute · Element Text Should Be ·Element Text Should Match · Element To String · Elements Should Be Equal · Elements Should Match · Evaluate Xpath · Get Child Elements · Get Element · Get Element Attribute · Get Element Attributes · Get Element Count ·Get Element Text · Get Elements · Get Elements Texts · Log Element · Parse Xml · Remove Element · Remove Element Attribute · Remove Element Attributes · Remove Elements · Remove Elements Attribute ·Remove Elements Attributes · Save Xml · Set Element Attribute · Set Element Tag · Set Element Text · Set Elements Attribute · Set Elements Tag · Set Elements Text

Keywords

Keyword Arguments Documentation
Add Element source, element,index=None, xpath=.

Adds a child element to the specified element.

The element to whom to add the new element is specified using source and xpath. They have exactly the same semantics as with Get Element keyword. The resulting XML structure is returned, and if the source is an already parsed XML structure, it is also modified in place.

The element to add can be specified as a path to an XML file or as a string containing XML, or it can be an already parsed XML element. The element is copied before adding so modifying either the original or the added element has no effect on the other . The element is added as the last child by default, but a custom index can be used to alter the position. Indices start from zero (0 = first position, 1 = second position, etc.), and negative numbers refer to positions at the end (-1 = second last position, -2 = third last, etc.).

Examples using ${XML} structure from Example:

Add Element ${XML}    
Add Element ${XML} xpath=new  
Add Element ${XML} index=1 xpath=new
${new} = Get Element ${XML} new  
Elements Should Be Equal ${new}    

Use Remove Element or Remove Elements to remove elements.

Clear Element source, xpath=.,clear_tail=False

Clears the contents of the specified element.

The element to clear is specified using source and xpath. They have exactly the same semantics as with Get Element keyword. The resulting XML structure is returned, and if the source is an already parsed XML structure, it is also modified in place.

Clearing the element means removing its text, attributes, and children. Element's tail text is not removed by default, but that can be changed by giving clear_tail a true value (see Boolean arguments). See Element attributes section for more information about tail in general.

Examples using ${XML} structure from Example:

Clear Element ${XML} xpath=first    
${first} = Get Element ${XML} xpath=first  
Elements Should Be Equal ${first}    
Clear Element ${XML} xpath=html/p/b clear_tail=yes  
Element Text Should Be ${XML} Text with italics. xpath=html/p normalize_whitespace=yes
Clear Element ${XML}      
Elements Should Be Equal ${XML}    

Use Remove Element to remove the whole element.

Copy Element source, xpath=.

Returns a copy of the specified element.

The element to copy is specified using source and xpath. They have exactly the same semantics as with Get Element keyword.

If the copy or the original element is modified afterwards, the changes have no effect on the other.

Examples using ${XML} structure from Example:

${elem} = Get Element ${XML} xpath=first
${copy1} = Copy Element ${elem}  
${copy2} = Copy Element ${XML} xpath=first
Set Element Text ${XML} new text xpath=first
Set Element Attribute ${copy1} id new
Elements Should Be Equal ${elem} new text  
Elements Should Be Equal ${copy1} text  
Elements Should Be Equal ${copy2} text  
Element Attribute Should Be source, name, expected,xpath=., message=None

Verifies that the specified attribute is expected.

The element whose attribute is verified is specified using source and xpath. They have exactly the same semantics as with Get Element keyword.

The keyword passes if the attribute name of the element is equal to the expected value, and otherwise it fails. The default error message can be overridden with the message argument.

To test that the element does not have a certain attribute, Python None (i.e. variable ${NONE}) can be used as the expected value. A cleaner alternative is using Element Should Not Have Attribute.

Examples using ${XML} structure from Example:

Element Attribute Should Be ${XML} id 1 xpath=first
Element Attribute Should Be ${XML} id ${NONE}  

See also Element Attribute Should Match and Get Element Attribute.

Element Attribute Should Match source, name, pattern,xpath=., message=None

Verifies that the specified attribute matches expected.

This keyword works exactly like Element Attribute Should Be except that the expected value can be given as a pattern that the attribute of the element must match.

Pattern matching is similar as matching files in a shell, and it is always case-sensitive. In the pattern, '*' matches anything and '?' matches any single character.

Examples using ${XML} structure from Example:

Element Attribute Should Match ${XML} id ? xpath=first
Element Attribute Should Match ${XML} id c*d xpath=third/second
Element Should Exist source, xpath=.,message=None

Verifies that one or more element match the given xpath.

Arguments source and xpath have exactly the same semantics as with Get Elements keyword. Keyword passes if the xpath matches one or more elements in the source. The default error message can be overridden with the message argument.

See also Element Should Not Exist as well as Get Element Count that this keyword uses internally.

Element Should Not Exist source, xpath=.,message=None

Verifies that no element match the given xpath.

Arguments source and xpath have exactly the same semantics as with Get Elements keyword. Keyword fails if the xpath matches any element in the source. The default error message can be overridden with the message argument.

See also 

你可能感兴趣的:(python,xhtml,操作系统)