The requirement is to run a Robot keyword which has named arg as dictionary, from PYTHON
But when we call like below, what i expect is a dictionary for arg1 and a string 'hello' in arg2, when calling "Test KW", but i get is, whole dictionary in arg1, and arg2 as empty
Need your help. Below is the minimal working code
cat testcase.robot
*** Settings ***
Library /homes/user/importresource.py
*** Test Cases ***
TC
custom keyword
cat test.robot
*** Keywords ***
Test KW
[Arguments] ${arg1}= ${arg2}=
${newvr}= set variable ${arg1}
${type value}= Evaluate type($arg1)
Log To Console ${type value}
Log to console ${arg2}
:FOR ${key} IN #{arg1.keys()}
\ Set To Dictionary ${args}
... ${key}=${arg1["${key}"]}
cat importresource.py
from robot.libraries.BuiltIn import BuiltIn
class importresource(object):
ROBOT_LISTENER_API_VERSION = 3
ROBOT_LIBRARY_SCOPE = 'TEST SUITE'
def __init__(self):
pass
def custom_keyword(self):
BuiltIn().import_resource('/homes/user/test.robot')
args = {'arg1':{'key':'value'},'arg2':'hello'}
BuiltIn().run_keyword('Test KW',args)
Related
I'm new to robot framework, I have a python script that I want to run in robot framework but it's not running properly, it fails and the output is empty of the script(the error: '' does not contain 'hello')
the robot framework file:
*** Settings ***
Library OperatingSystem
Library Process
*** Test Cases ***
Simple pyhton example
${result} = Run Process python3 hello.py -c arg1 -b arg2
Log ${result.stdout}
Should Contain ${result.stdout} hello
the py file:
import sys
print("This is the name of the script:", sys.argv[0])
print("Number of arguments:", len(sys.argv))
print("The arguments are:", str(sys.argv))
print("hello world")
First of all your hello.py file needs to contain a function (or a class with methods). Not sure what you want to do with the specific arguments, but the content could look like this:
def hello_world(name, friend):
return "hello " + name + " and " + friend
Now you may import it as a library into your robot file and call the function:
*** Settings ***
Library hello.py
*** Test Cases ***
Simple Pyhton Example
${result} Hello World bitrex superman
Log ${result}
Should Contain ${result} hello
More information: https://robotframework.org/robotframework/latest/RobotFrameworkUserGuide.html#creating-test-libraries
I am using Invoke and have two tasks: one cleans a raw data file to produce a clean data file, and the other produces several plots from the clean data file:
RAW_FILE = "raw.csv"
CLEAN_FILE = "clean.csv"
PLOT_FILE = "plot.svg"
#task(optional=["logging"])
def clean_data(c, logging=None):
"""Produce cleaned-up dataset."""
print("CLEAN", logging)
_configure_logging(logging)
df = clean_raw_data(RAW_FILE)
df.to_csv(CLEAN_FILE, index=False)
#task(pre=[clean_data], optional=["logging"])
def plot_data(c, logging=None):
"""Create plots of data."""
print("PLOT", logging)
_configure_logging(logging)
make_plot(CLEAN_FILE, PLOT_FILE)
def _configure_logging(log_level):
"""Initialize logging."""
if log_level is not None:
print("SETTING LOGGING TO", log_level)
CONFIG["LOGGING_LEVEL"] = log_level.upper()
If I run:
$ invoke clean-data --logging info
then logging is set to INFO and I get a message from inside clean_raw_data. However, if I run:
$ invoke plot-data --logging info
then:
clean_data is invoked with logging=None, so no log message appears.
plot_data is then invoked with logging="info", so its log message appears.
My expectation was that command-line flags would be passed down to dependent tasks. I tried doing this manually:
#task(pre=[call(clean_data, logging=logging)], optional=["logging"])
def plot_data(c, logging=None):
...as before...
but this produces an error message because logging isn't defined at the point the #task decorator is invoked.
Is there a way to chain optional arguments in the desired fashioned?
I'm parameterizing pytest tests with variables defined in an external YAML file using the pytest_generate_tests hook. The name of the variable file is specified on the pytest command line (--params_file). Only some of the test functions within a module are parameterized and require the variables in this file. Thus, the command line option defining the variables is an optional argument. If the optional argument is omitted from the command line, then I want pytest to just "skip" those test functions which need the external parameterized variables and just run the "other" tests which are not parameterized. The problem is, if the command line option is omitted, pytest is skipping ALL of the test functions, not just the test functions that require the parameters.
Here is the test module file:
def test_network_validate_1(logger, device_connections,):
### Test code omitted.....
def test_lsp_throttle_timers(params_file, logger, device_connections):
### Test code omitted.....
def test_network_validate_2(logger, device_connections,):
### Test code omitted.....
pytest_generate_tests hook in conftest.py:
# Note, I tried scope at function level as well but that did not help
#pytest.fixture(scope='session')
def params_file(request):
pass
def pytest_generate_tests(metafunc):
### Get Pytest rootdir
rootdir = metafunc.config.rootdir
print(f"*********** Test Function: {metafunc.function.__name__}")
if "params_file" in metafunc.fixturenames:
print("*********** Hello Silver ****************")
if metafunc.config.getoption("--params_file"):
#################################################################
# Params file now located as a separate command line argument for
# greater flexibility
#################################################################
params_file = metafunc.config.getoption("--params_file")
params_doc = dnet_generic.open_yaml_file(Path(rootdir, params_file),
loader_type=yaml.Loader)
test_module = metafunc.module.__name__
test_function = metafunc.function.__name__
names,values = dnet_generic.get_test_parameters(test_module,
test_function,
params_doc,)
metafunc.parametrize(names, values )
else:
pytest.skip("This test requires the params_file argument")
When the params_file option is present, everything works fine:
pytest isis/test_isis_lsp_throttle.py --testinfo topoA_r28.yml --ulog -s --params_file common/topoA_params.yml --collect-only
===================================================================================== test session starts =====================================================================================
platform linux -- Python 3.7.4, pytest-3.7.0, py-1.8.0, pluggy-0.13.0
rootdir: /home/as2863/pythonProjects/p1-automation, inifile: pytest.ini
plugins: csv-2.0.1, check-0.3.5, pylama-7.6.6, dependency-0.4.0, instafail-0.4.0, ordering-0.6, repeat-0.7.0, reportportal-5.0.3
collecting 0 items *********** Test Function: test_network_validate_1
*********** Test Function: test_lsp_throttle_timers
*********** Test Function: test_network_validate_2
collected 3 items
<Package '/home/as2863/pythonProjects/p1-automation/isis'>
<Module 'test_isis_lsp_throttle.py'>
<Function 'test_network_validate_1'>
<Function 'test_lsp_throttle_timers'>
<Function 'test_network_validate_2'>
================================================================================ no tests ran in 0.02 seconds =================================================================================
When the params_file option is ommitted, you can see that no tests are run and the print statement shows it does not even try to run pytest_generate_tests on "test_network_validate_2"
pytest isis/test_isis_lsp_throttle.py --testinfo topoA_r28.yml --ulog -s --collect-only ===================================================================================== test session starts =====================================================================================
platform linux -- Python 3.7.4, pytest-3.7.0, py-1.8.0, pluggy-0.13.0
rootdir: /home/as2863/pythonProjects/p1-automation, inifile: pytest.ini
plugins: csv-2.0.1, check-0.3.5, pylama-7.6.6, dependency-0.4.0, instafail-0.4.0, ordering-0.6, repeat-0.7.0, reportportal-5.0.3
collecting 0 items
*********** Test Function: test_network_validate_1
*********** Test Function: test_lsp_throttle_timers
*********** Hello Silver ****************
collected 0 items / 1 skipped
================================================================================== 1 skipped in 0.11 seconds ==================================================================================
As has been found in the discussion in the comments, you cannot use pytest.skip in pytest_generate_tests, because it will work on module scope. To skip the concrete test, you can do something like this:
#pytest.fixture
def skip_test():
pytest.skip('Some reason')
def pytest_generate_tests(metafunc):
if "params_file" in metafunc.fixturenames:
if metafunc.config.getoption("--params_file"):
...
metafunc.parametrize(names, values )
else:
metafunc.fixturenames.insert(0, 'skip_test')
E.g. you introduce a fixture that will skip the concrete test, and add this fixture to the test. Make sure to insert it as the first fixture, so no other fixtures will be executed.
While MrBean Bremen's answer may work, according to the pytest authors dynamically altering the fixture list is not something they really want to support. This approach, however, is a bit more supported.
# This is "auto used", but doesn't always skip the test unless the test parameters require it
#pytest.fixture(autouse=True)
def skip_test(request):
# For some reason this is only conditionally set if a param is passed
# https://github.com/pytest-dev/pytest/blob/791b51d0faea365aa9474bb83f9cd964fe265c21/src/_pytest/fixtures.py#L762
if not hasattr(request, 'param'):
return
pytest.skip(f"Test skipped: {request.param}")
And in your test module:
def _add_flag_parameter(metafunc: pytest.Metafunc, name: str):
if name not in metafunc.fixturenames:
return
flag_value = metafunc.config.getoption(name)
if flag_value:
metafunc.parametrize(name, [flag_value])
else:
metafunc.parametrize("skip_test", ["Missing flag '{name}'"], indirect=True)
def pytest_generate_tests(metafunc: pytest.Metafunc):
_add_flag_parameter(metafunc, "params_file")
i have a python class Wiresharking.py
from subprocess import Popen, CREATE_NEW_CONSOLE,PIPE,STDOUT
import time
import subprocess
import datetime
import os
#import envSI
class Wiresharking:
"""Wireshark Server subclass"""
def __init__(self,**kwargs):
self.filters=''
self.window_ip = kwargs.get('ip')
print type(self.window_ip)
self.window_user= kwargs.get('username')
self.window_password= kwargs.get('password')
self.dest_path= kwargs.get('Target_path')
self.interface= kwargs.get('interface')
self.terminal='cmd'
self.home=kwargs.get('Home_path')
def test(self):
print 'hi'
return self.window_ip
i can call it from another python file (env.py) like below
SERVER_01 = Wiresharking(
name='WIRESHARK_ENV91',
ip='192.168.1.16',
username=r'INTRA\pmmm', #always prepend r , before giving your username and password
password='jan#2018',
prompt='$ ',
autostart=False,
unzip_capture=True,
filter='',
#interface=['ens2f0'],
interface='Ethernet',
Target_path=r'D:\Users\pankaj-m\Desktop\Test'
)
print SERVER_01.test()
output :
<type 'str'>
hi
192.168.1.16
however , the problem arises when i use env.py file as --variable file with robotframework
command
pybot -V env.py Check.robot
Check.robot file is below
*** Settings ***
Library Wiresharking.py
*** Test Cases ***
Test
check
*** Keywords ***
check
${abc} = test
log ${abc}
the output i here getting is 'None'
16:13:37.279 INFO None
can anyone point out what wrong i am doing here.
Your env.py defines a single variable named ${SERVER_01}. However, Check.robot never uses that variable.
Check.robot imports Wiresharking.py without passing any arguments. That causes its self.window_ip to be None, and thus the keyword returns None.
If you want to see the values from env.py, you need to look at the ${SERVER_01} variable. For example:
log ${SERVER_01.window_ip}
This is the way i was able to pass **kwargs and able to resolve error .
I am still looking for a cleaner way to pass **kwargs
*** Settings ***
Library Wiresharking.py ip=${SERVER_01.window_ip} username=${SERVER_01.window_user} password=${SERVER_01.window_password} Target_path=${SERVER_01.dest_path} interface=${SERVER_01.interface} Home_path=${SERVER_01.home} WITH NAME Mylib
*** Variables ***
${window_ip }
#&{names} = Create Dictionary 0=First Name 2=Email
*** Test Cases ***
Test
check
*** Keywords ***
check
${abc} = test
log ${abc}
Output
INFO 192.168.1.16
In robot framework how do you to create object of class and call the methods in corresponding class? This is the code snippet.
*** Settings ***
Documentation A resource file with reusable keywords and variables.
... Use keywords in this file in testcases directory.
Library /home/kirti/src/Helper/utilities.py
Library /home/kirti/src/Helper/config_parser.py
#Library /home/kirti/qa/src/executor/cleanup.CleanUp
Library /home/kirti/qa/src/executor/cleanup.py
*** Variables ***
${RESULT} 0
*** Keywords ***
Read Json Config Values
Log To Console "Setting up the config values globally"
config_parser.Json Config Parser
Import Variables /home/kirti/src/Helper/variables.py
Log Variables INFO
Check Machines Reachability
utilities.Check All Machines Status
Check SNMP Counter
utilities.Get Snmp 192.178.1.2 PPSessionCount
Call Clean Up
#${cleanupobj}= cleanup.create cleanup
#${name}= ${cleanupobj.cc()}
Import Library /home/kirti/src/executor/cleanup.py
${cmp}= Get library instance CleanUp
Log To Console ${cmp}.__class__.__name__
#${name}= Call method ${cmp} Create cleanup
${name}= Call method ${cmp} cc
#${name}= Call method ${cleanupobj} env cleanup
#Log To Console "${name}"
#Log Variables INFO
utilities.Check All Machines Status
Here is a way you can achieve the desired result.
Lets take example of demo.py which have class Sample
Sample class has init ,getting_path() as methods
class Sample(object):
def __init__(self,path,device):
self.device=device
self.path = path
def getting_path(self):
return self.path
Lets use these methods in Robotfile
*** Settings ***
#in the Library section you reference python class in below format
# (file.class_name) so file is demo.py and class is Sample
Library demo.Sample ${path} ${device} WITH NAME obj
#path and device are two arguments required by __init__,'obj' will be used to
#access the methods in python class
Library Collections
*** Variables ***
${path} c:
${device} samsung
*** Test Cases ***
Test
Test_python_class
*** Keywords ***
Test_python_class
#with obj you now call the method of python file
${result} = obj.getting_path
#if method need any argument , this can be passed like
#${result} = obj.getting_path ${arg1} ${arg2}
log to console ${result}
If you want to use a specific instance of a class you can use
${instance} = obj arg1
log to console ${instance.function(args)}