I got a very specific scenario, where I'm inserting some data to the database(e.g. let's say 3 inserts and each one of them returns some ID) and based on a return value I want to create dynamically test cases for those return values E.g.
*** Variables ***
@{result} ${EMPTY}
*** Test Cases ***
Some dummy sql inserts
${result} Insert sql statements dt1 dt2 dt3 #e.g. return ['123', '456', '789']
Verify some ids
# NOPE, sorry i can't use [Template] because each iteration is not marked on a report as a "TEST" but as a "VAR"
Verify if ids exist somewhere ${result} #This keyword execution should create another 3 test cases, one for each item from ${result} list
*** Keywords ***
Insert sql statement
[Arguments] @{data}
Create List ${result}
FOR ${elem} IN @{data}
${return_id} SomeLib.Execute SQL INSERT INTO some_table(some_id) VALUES (${elem})
Append To List ${result} ${return_id}
END
[Return] ${result}
Verify if ids exist somewhere
[Arguments] ${some_list_of_ids}
FOR ${id} IN @{some_list_of_ids}
So some stuff on ${id}
END
I was trying to figure out how to do that by reffering to robot API documentation but without any success.
Can you please tell/advise if it's feasible, if so, than how can I achieve that.
So far I've figured out that there might be 2 ways of doing this:
In both cases, I have to put the logic there, but can't figure out how to create test cases on-the-fly.
Help, please? :)
P.S. Some examples are more than welcome. Thanks in advance
There is a blog post with a answer for you: https://gerg.dev/2018/09/dynamically-create-test-cases-with-robot-framework/
As you suggested the solution is to create a listener so you can add tests dynamically. Just carefully read the post as there are some constrains as when you can and cannot create tests (during the execution process). Also, the post was for 3.x framework and for 4.x you need to make a tiny change in the class, by replacing: tc.keywords.create(name=kwname, args=args) with: tc.body.create_keyword(name=kwname, args=args).
Example on how to implement this:
demo.robot:
*** Settings ***
Library DynamicTestCases.py
*** Test Cases ***
Create Dynamic Test Cases
@{TestNamesList} Create List "Test 1" "Test 2" "Test 3"
FOR ${element} IN @{TestNamesList}
Add Test Case ${element} Keyword To Execute
END
*** Keywords ***
Keyword To Execute
Log This is executed for each test!
DynamicTestCases.py contents (basic copy from the url I posted + the changed line):
from __future__ import print_function
from robot.running.model import TestSuite
class DynamicTestCases(object):
ROBOT_LISTENER_API_VERSION = 3
ROBOT_LIBRARY_SCOPE = 'TEST SUITE'
def __init__(self):
self.ROBOT_LIBRARY_LISTENER = self
self.current_suite = None
def _start_suite(self, suite, result):
# save current suite so that we can modify it later
self.current_suite = suite
def add_test_case(self, name, kwname, *args):
"""Adds a test case to the current suite
'name' is the test case name
'kwname' is the keyword to call
'*args' are the arguments to pass to the keyword
Example:
add_test_case Example Test Case
... log hello, world WARN
"""
tc = self.current_suite.tests.create(name=name)
#tc.keywords.create(name=kwname, args=args) #deprecated in 4.0
tc.body.create_keyword(name=kwname, args=args)
# To get our class to load, the module needs to have a class
# with the same name of a module. This makes that happen:
globals()[__name__] = DynamicTestCases
This is a small example how to make it work. If for example you want to give a variable to the keyword, just add the argument:
*** Settings ***
Library DynamicTestCases.py
*** Test Cases ***
Create Dynamic Test Cases
@{TestNamesList} Create List "Test 1" "Test 2" "Test 3"
FOR ${element} IN @{TestNamesList}
Add Test Case ${element} Keyword To Execute ${element}
END
*** Keywords ***
Keyword To Execute
[Arguments] ${variable}
Log The variable sent to the test was: ${variable}