robotframework-tools
robotframework-tools copied to clipboard
[DRAFT >> https://github.com/zimmermanncode/robotframework-tools] Python Tools for Robot Framework and Test Libraries
Robot Framework Tools
Python Tools for Robot Framework and Test Libraries.
-
A
testlibraryframework for creating Dynamic Test Libraries. -
A
ContextHandlerframework fortestlibraryto create switchable sets of different Keyword implementations. -
A
SessionHandlerframework fortestlibraryto auto-generate Keywords for session management. -
A
TestLibraryInspector. -
An interactive
TestRobot. -
A
RemoteRobot, combiningTestRobotwith externalRobotRemoteServer -
A
ToolsLibrary, accompanying Robot Framework's standard Test Libraries. -
A
robotshellextension for IPython.
0. Setup
Supported Python versions: 2.7.x, 3.3.x and later
Package name: robotframework-tools
Package extra features:
- [remote]:
RemoteRobot - [robotshell]
Requirements
six>=1.10path.py>=8.0moretools>=0.1.8robotframework>=2.8.7- Python 3.x:
- Robot Framework 3.0+ is officially Python 3 compatible
- For earlier versions please install
robotframework-python3
Extra requirements for [remote]:
Extra requirements for [robotshell]:
Installation
python setup.py install
Or with pip:
pip install .
Or from PyPI:
pip install robotframework-tools
-
With all extra features:
pip install robotframework-tools[remote,robotshell] -
Robot Framework will not be installed automatically
1. Creating Dynamic Test Libraries
from robottools import testlibrary
TestLibrary = testlibrary()
Defined in a module also called TestLibrary,
this generated Dynamic TestLibrary type
could now directly be imported in Robot Framework.
It features all the required methods:
get_keyword_namesget_keyword_argumentsget_keyword_documentationrun_keyword
Keywords
The TestLibrary has no Keywords so far...
To add some just use the TestLibrary.keyword decorator:
@TestLibrary.keyword
def some_keyword(self, arg, *rest):
...
A keyword function can be defined anywhere in any scope.
The TestLibrary.keyword decorator
always links it to the TestLibrary
(but always returns the original function object).
And when called as a Keyword from Robot Framework
the self parameter will always get the TestLibrary instance.
You may want to define your keyword methods
at your Test Library class scope.
Just derive your actual Dynamic Test Library class from TestLibrary:
# SomeLibrary.py
class SomeLibrary(TestLibrary):
def no_keyword(self, ...):
...
@TestLibrary.keyword
def some_other_keyword(self, arg, *rest):
...
To get a simple interactive SomeLibrary overview just instantiate it:
In : lib = SomeLibrary()
You can inspect all Keywords in Robot CamelCase style (and call them for testing):
In : lib.SomeKeyword
Out: SomeLibrary.Some Keyword [ arg | *rest ]
By default the Keyword names and argument lists are auto-generated from the function definition. You can override that:
@TestLibrary.keyword(name='KEYword N@me', args=['f|r$t', 'se[ond', ...])
def function(self, *args):
...
Keyword Options
When you apply custom decorators to your Keyword functions
which don't return the original function objects,
you would have to take care of preserving the original argspec for Robot.
testlibrary can handle this for you:
def some_decorator(func):
def wrapper(...):
return func(...)
# You still have to take care of the function(-->Keyword) name:
wrapper.__name__ = func.__name__
return wrapper
TestLibrary = testlibrary(
register_keyword_options=[
# Either just:
some_decorator,
# Or with some other name:
('some_option', some_decorator),
],
)
@TestLibrary.keyword.some_option
def some_keyword_with_options(self, arg, *rest):
...
There are predefined options. Currently:
unicode_to_str- Converts allunicodevalues (pybot's default) tostr.varargs_to_kwargs- Moves items pairwise from*varargsto**kwargs.kwargs_from_strings- Splits anykey=valuestrings in*varargsand moves them to**kwargs.keys_from_vars- Substitutes variable keys in${key}=valueitems in**kwargs.
You can specify default_keyword_options that will always be applied:
TestLibrary = testlibrary(
register_keyword_options=[
('some_option', some_decorator),
],
default_keyword_options=[
'unicode_to_str',
'some_option',
)
To bypass the default_keyword_options for single Keywords:
@TestLibrary.keyword.no_options
def some_keyword_without_options(self, arg, *rest):
...
@TestLibrary.keyword.reset_options.some_option
def some_keyword_without_default_options(self, arg, *rest):
...
1.1 Adding switchable Keyword contexts
from robottools import ContextHandler
TODO...
1.2 Adding session management
from robottools import SessionHandler
Whenever your Test Library needs to deal with sessions, like network connections, which you want to open, switch, close, and when you don't always want to specify the actual session to use as a Keyword argument, just do:
class SomeConnection(SessionHandler):
# All methods starting with `open`
# will be turned into session opener Keywords.
# `self` will get the Test Library instance.
def open(self, host, *args):
return internal_connection_handler(host)
def open_in_a_different_way(self, host):
return ...
TestLibrary = testlibrary(
session_handlers=[SomeConnection],
)
The following Keywords will be generated:
TestLibrary.Open Some Connection [ host | *args ]TestLibrary.Open Named Some Connection [ alias | host | *args ]TestLibrary.Open Some Connection In A Different Way [ host ]TestLibrary.Open Named Some Connection In A Different Way [ alias | host ]TestLibrary.Swith Some Connection [ alias ]TestLibrary.Close Some Connection [ ]
You can access the currently active session instance, as returned from an opener Keyword, with an auto-generated property based on the handler class name:
@TestLibrary.keyword
def some_keyword(self):
self.some_connection.do_something()
If there is no active session,
a TestLibrary.SomeConnectionError will be raised.
Close Some Connection will only release all references
to the stored session object.
To add custom logic just add a close method to your SessionHandler:
class SomeConnection(SessionHandler):
...
def close(self, connection):
# `self` will get the Test Library instance.
...
The SessionHandler framework additionally supports some Meta options:
class SomeConnection(SessionHandler):
class Meta:
# options
...
-
auto_explicit = Truewill automatically modify every Keyword of the Test Library to support explicit session switching with an additional named argument based on the handler class name. After the Keyword call, the session will switch back to the previously active one:Some Keyword ... some_connection=alias
2. Inspecting Test Libraries
from robottools import TestLibraryInspector
Now you can load any Test Library in two ways:
builtin = TestLibraryInspector('BuiltIn')
oslib = TestLibraryInspector.OperatingSystem
TODO...
3. Using Robot Framework interactively
from robottools import TestRobot
test = TestRobot('Test')
The TestRobot basically uses the same Robot Framework internals
for loading Test Libraries and running Keywords
as pybot and its alternatives,
so you can expect the same behavior from your Keywords.
All functionalitiy is exposed in CamelCase:
test.Import('SomeLibrary')
TODO...
4. Using Robot Framework remotely
from robottools.remote import RemoteRobot
RemoteRobot is derived from robottools.TestRobot
and external robotremoteserver.RobotRemoteServer,
which is derived from Python's SimpleXMLRPCServer.
The __init__() method shares most of its basic arguments
with RobotRemoteServer:
def __init__(
self, libraries, host='127.0.0.1', port=8270, port_file=None,
allow_stop=True, allow_import=None,
register_keywords=True, introspection=True,
):
...
The differences:
- Instead of a single pre-initialized Test Library instance,
you can provide a sequence of multiple Test Library names,
which will be imported and initialized using
TestRobot.Import(). - The additional argument
allow_importtakes a sequence of Test Library names, which can later be imported remotely via theImport Remote LibraryKeyword described below. RemoteRobotalso directly registers Keywords as remote methods (RobotRemoteServeronly registers a Dynamic Library API). You can change this by settingregister_keywords=False.RemoteRobotcallsSimpleXMLRPCServer.register_introspection_functions(). You can change this by settingintrospection=False.
Once initialized the RemoteRobot will immediately start its service.
You can connect with any XML-RPC client
like Python's xmlrpc.client.ServerProxy
(Python 2.7: xmlrpclib.ServerProxy).
To access the RemoteRobot from your Test Scripts,
you can use Robot Framework's standard Remote Library.
Once connected it will provide all the Keywords from the Test Libraries
imported by the RemoteRobot.
Besides RobotRemoteServer's additional Stop Remote Server Keyword
RemoteRobot further provides these extra Keywords:
-
Import Remote Library [ name ]Remotely import the Test Library with given
name.Does the same remotely as
BuiltIn.Import Librarydoes locally. The Test Library must be allowed on server side.The
Remoteclient library must be reloaded to make the new Keywords accessible. This can be done withToolsLibrary.Reload Library.
5. Using the ToolsLibrary
The ToolsLibrary is a Dynamic Test Library,
which provides these additional general purpose Keywords:
-
Reload Library [ name | *args]Reload an already imported Test Library with given
nameand optionalargs.This also leads to a reload of the Test Library Keywords, which allows Test Libraries to dynamically extend or change them.
The ToolsLibrary is based on robottools.testlibrary.
To use it directly in Python:
from ToolsLibrary import ToolsLibrary
tools = ToolsLibrary()
Then you can call the Keywords in tools.CamelCase(...) style.
6. Using IPython as a Robot Framework shell
In : %load_ext robotshell
Now all the robottools.TestRobot functionality
is exposed as IPython %magic functions...
[Robot.Default]
In : %Import SomeLibrary
Out: [Library] SomeLibrary
As with a robottools.TestRobot you can call Keywords
with or without the Test Library prefix.
You can simply assign the return values to normal Python variables.
And there are two ways of separating the arguments:
[Robot.Default]
In : ret = %SomeKeyword value ...
[TRACE] Arguments: [ 'value', '...' ]
[TRACE] Return: ...
[Robot.Default]
In : ret = %SomeLibrary.SomeOtherKeyword | with some value | ...
[TRACE] Arguments: [ 'with some value', '...' ]
[TRACE] Return: ...
You can create new Robots and switch between them:
[Robot.Default]
In : %Robot Test
Out: [Robot] Test
[Robot.Test]
In : %Robot.Default
Out: [Robot] Default
[Robot.Default]
In :
If a Keyword fails the traceback is just printed like in a Robot Log.
If it fails unexpectedly you may want to debug it.
Just turn on %robot_debug mode
and the Keyword's exception will be re-raised.
Combine it with IPython's automatic %pdb mode
and you'll get a nice Test Library debugging environment.
Variables
Robot Framework uses ${...} and @{...} syntax for accessing variables.
In %magic function call parameters
IPython already substitutes Python variables inside {...}
with their str() conversion.
This conflicts with Robot variable syntax.
To access a Robot variable you need to use double braces:
%Keyword ${{var}}
Or to expand a list variable:
%Keyword @{{listvar}}
This way you can also pass Python variables directly to a Robot Keyword.
If the Robot can't find the variable in its own dictionary,
lookup is first extended to IPython's user_ns (shell level)
and finally to Python's builtins.