Flow Control¶
This section focuses on script execution control features offered in aetest
.
Control features allows users to master the flow of the testscript, performing
actions such as breaking the execution continuity and jumping ahead, skipping
or only executing specific testcases by uid
, grouping testcases together,
etc.
Control features described in this section are entirely optional.
Skip Conditions¶
aetest
comes out-of-box with a few built-in preprocessors defined using
Section Processors feature, intended to be used as shortcuts to skipping
test sections.
@aetest.skip(reason = 'message')
unconditionally skip the decorated section. reason should describe why that section is being skipped.
aetest.skip.affix(testcase, reason)
Same as the skip decorator, but can be used on the fly. So depending on one testcase result, the other testcases can be skipped.
@aetest.skipIf(condition, reason = 'message')
skip the decorated test section if condition is
True
.aetest.skipIf.affix(testcase, condition, reason)
It can be used to assign skipIf decorator to the testcases, condition can be a callable or a boolean.
@aetest.skipUnless(condition, reason = 'message')
skip the decorated test section unless condition is
True
.aetest.skipUnless.affix(testcase, condition, reason)
Can be used on the fly to assign decorators to the testcases
# Example
# -------
#
# skip/skipIf/skipUnless examples
from pyats import aetest
# skip testcase intentionally
@aetest.skip('because we had to')
class Testcase(aetest.Testcase):
pass
class TestcaseTwo(aetest.Testcase):
# skip test section using if library version < some number
@aetest.skipIf(mylibrary.__version__ < (1, 3),
'not supported in this library version')
@aetest.test
def test_one(self):
pass
# skip unless library version > some number
@aetest.skipUnless(mylibrary.__version__ > (1, 3),
'not supported in this library version')
@aetest.test
def test_two(self):
pass
@aetest.test
def test_three(self):
aetest.skip.affix(section = TestcaseTwo.test_four,
reason = "message")
aetest.skipIf.affix(section = TestcaseTwo.test_five,
condition = True,
reason = "message")
aetest.skipUnless.affix(section = TestcaseThree,
condition = False,
reason = "message")
@aetest.test
def test_four(self):
# will be skipped because of test_three
pass
@aetest.test
def test_five(self):
# will be skipped because of test_three
pass
@aetest.test
def test_six(self):
# will be skipped because of test_three
pass
class TestcaseThree(aetest.Testcase):
# will be skipped because of TestcaseTwo.test_three
pass
Tip
the above skip decorators only support boolean conditions. Use the full
Section Processors feature if your skip condition are functions that
requires access to section
objects.
Run IDS¶
Run uids
is the concept of only executing testcases & sections with uids
that matches up to a particular requirement. It provides users direct, finer
control before and during execution, over which sections to run, and which
sections to skip over.
There are two methods of providing aetest
with this requirement:
through Standard Arguments as part of script run arguments, or,
by setting runtime variable,
runtime.uids
dynamically during execution.
uids
accepts a python callable
(eg, a function). The list of currently
running section uids are provided as inputs to this callable
, and if the
function evaluates to True
, the section is run, otherwise, the section is
not run and ignored completely so that it’s not displayed in the report.
# Example
# -------
#
# setting from jobfile example
from pyats.easypy import run
# function determining whether we should run testcase_A
# currently executing uids is always a list of:
# [ <container uid>, <section uid>]
# eg, ['common_setup', 'subsection_one']
# thus varargs (using *) is required for the function input.
def run_only_testcase_A(*ids):
# check that we are running testcase_A
return 'testcase_A' in uids
# run only testcase_A and its contents (using callable)
# executing uids has testcase_A:
run('example_script.py', uids = run_only_testcase_A)
The callable
provided to uids
feature is evaluated against the list of
current run ids, using varargs (eg, *uids
). This feature combined with
Logic Testing objects can provide quite a bit of control over which
testcases to run and which not to.
# Example
# -------
#
# setting from jobfile example, using logics
from pyats.easypy import run
# import the logic objects
from pyats.datastructures.logic import And, Or, Not
# run the testscript with a bunch of logic involved
# eg: both common_setup and common_cleanup,
# and all bgp and ospf non-sanity traffic tests.
run('example_script.py', uids = Or('common_setup',
And('^bgp.+', '.*traffic.*', Not('sanity')),
And('^ospf.+', '.*traffic.*', Not('sanity')),
'common_cleanup'))
# or, when this testscript is run, call it with -uids argument
# python example.py -uids="Or('common_setup', \
# And('^bgp.+', '.*traffic.*', Not('sanity')),\
# And('^ospf.+', '.*traffic.*', Not('sanity')),\
# 'common_cleanup')"
Current uids
requirements is stored and accessible using
runtime feature as runtime.uids
. This value is write enabled:
when dynamically set during runtime, it takes effect immediately starting
from the next section onwards.
# Example
# -------
#
# setting uids dynamically in testscript
from pyats import aetest
# using logics
from pyats.datastructures.logic import Not, And
class CommonSetup(aetest.CommonSetup):
@aetest.subsection
def subsection(self):
# set uids dynamically during runtime
# eg, run all tests with bgp in the name, but not traffic.
aetest.runtime.uids = And('.*bgp.*', Not('traffic'))
If both methods of providing uids
is used, then the engine first
starts up by following the list provided using
Standard Arguments, until the runtime variable
runtime.uids
is updated.
Testcase Grouping¶
Testcase grouping feature enables testcases to be “associated” together by certain keywords, allowing a testscript execution be limited to only running testcases of one or more particular groups that matches up to input criterion.
To use this feature, testcases needs to be labelled first with corresponding
groups by setting their groups
attribute. By default, testscases do not
belong to any groups (groups = []
).
# Example
# -------
#
# associating testcases to run groups
# syntax
#
# groups = [ <list of group names in str. no spaces> ]
from pyats import aetest
class Testcase(aetest.Testcase):
# associating this testcase to 3 separate groups
groups = ['group_A', 'group_B', 'group_C']
After labelling testcases to one or more groups, use one of the following methods to control which testcase groups are run:
using Standard Arguments
-groups
, or,by setting runtime variable,
runtime.groups
dynamically.
Like Run IDS feature, groups
also accepts a python callable
(eg, a function). Each testcase’s groups values are provided as inputs to this
callable
, and if the return value is True
, the testcase is run.
Otherwise, the testcase is not run and ignored completely so that it’s not
displayed in the report.
# Example
# -------
#
# setting groups from jobfile example
from pyats.easypy import run
# create a function that tests for testcase groups
# this api tests that a testcase belongs to sanity but not traffic.
# note that varargs (using *) is required, as the list of groups to each
# testcase is unknown.
def non_traffic_sanities(*groups):
return 'sanity' in groups and 'traffic' not in groups
# run the testscript by providing the above function to test groups
run('example_script.py', groups = non_traffic_sanities)
# --------------------------------------------------------------------------
# behind the scenes (pseudo code)
# for each testcase:
# if non_traffic_sanities(testcase.groups):
# run testcase
# else:
# pass
The callable
provided to groups feature is evaluated against the list of
groups each testcase belongs to, using varargs (eg, *groups
). This feature
is intended to be used in conjunction with Logic Testing objects.
# Example
# -------
#
# setting groups from jobfile example, using logics
from pyats.easypy import run
# import the logic objects
from pyats.datastructures.logic import And, Not
# same as above, run sanity non-traffic testcases
run('example_script.py', groups = And('sanity', Not('traffic')))
# or, from command line:
# python example.py -groups="And('sanity', Not('traffic'))"
Similarly, groups
can also be set dynamically during runtime:
# Example
# -------
#
# setting groups dynamically
from pyats import aetest
from pyats.datastructures.logic import And, Not
class CommonSetup(aetest.CommonSetup):
@aetest.subsection
def subsection(self):
# set groups dynamically during runtime
aetest.runtime.groups = And('sanity', Not('traffic'))
And takes effect immediately.
Grouping feature is only applicable to testcases. In essence, the control of whether testcases are run based on their grouping is handed entirely to the user via boolean truth testing. Users can read the current executing groups via runtime variable, and manipulate it based on a variety of other factors, etc.
Requisite Testcase¶
Requisite testcase feature allows users to mark important testcases as
must pass: if such testcases failed, the current script execution is
aborted automatically by jumping forward to CommonCleanup
section using
Goto.
To mark testcases as requisite or must pass, set its attribute must_pass
to True
. By default, no testcase is “requisite”, eg, must_pass = False
.
# Example
# -------
#
# must_pass feature demonstration
from pyats import aetest
class TestcaseOne(aetest.Testcase):
must_pass = True
@aetest.test
def test(self):
self.failed('boom!')
class TestcaseTwo(aetest.Testcase):
pass
class CommonCleanup(aetest.CommonCleanup)
@aetest.subsection
def subsection(self):
pass
# output result
#
# SECTIONS/TESTCASES RESULT
# --------------------------------------------------------------------------
# .
# |-- TestcaseOne FAILED
# | `-- test FAILED
# |-- TestcaseTwo BLOCKED
# `-- common_cleanup PASSED
# `-- subsection PASSED
Note
the use of testcase must_pass
flag does not imply testcase dependency
It only marks a testcase as “critical” in terms of importance, where, if it
failed then the rest of the execution should be abandoned.
Testcase Randomization¶
Testcase randomization feature allows testcases within each testscript to be executed in pseudo-random order. This allows for mixed script coverage trails, yielding to better overall testing over time.
By default, testcase randomization is turned off, and all testcases are executed
in the order described by Section Ordering. To turn on
randomization, set Standard Arguments random=True
. This
causes all testcases within the current testscript to be shuffled before
execution.
Normally, the system automatically picks a random integer value to seed the
python randomizer. This value is always printed in the log. This behavior can be
averted by setting the optional argument random_seed
to a integer value,
fixing the outcome. This might be highly useful when trying to reproduce an
issue caused by a particular randomized testcase order: simply reuse the
seed value as shown in the logfile, and the same “randomized” order is
repeated.
# Example
# -------
#
# randomization of testcases feature demonstration
from pyats import aetest
# define a couple testcases
class TestcaseOne(aetest.Testcase):
pass
class TestcaseTwo(aetest.Testcase):
pass
class TestcaseThree(aetest.Testcase):
pass
# run it
aetest.main(random = True)
# output result
#
# Testcase randomization is enabled, seed: 1461425170
#
# SECTIONS/TESTCASES RESULT
# --------------------------------------------------------------------------
# .
# |-- TestcaseTwo PASSED
# |-- TestcaseOne PASSED
# `-- TestcaseThree PASSED
Note
testcase randomization only affects the order of Testcases
.
CommonSetup
and CommonCleanup
continue to run before and after
all testcases, respectively.
Tip
testcase randomization feature should be turned off before submitting testscript to regression.
Maximum Failures¶
The maximum failures feature allows users to put a limit to the number of testcase failures in a testscript before the script auto-aborts. This feature is intended to safeguard against massive failures in long-running scripts, and avoids wasting valuable testbed runtime when something goes horribly wrong.
To use this feature, set Standard Arguments max_failures
to an
integer value. During execution, if the total number of failed Testcases
reaches the provided value, the script auto-aborts by jumping forward to
CommonCleanup
section using Goto.
# Example
# -------
#
# maximum failures feature demonstration
from pyats import aetest
class TestcaseOne(aetest.Testcase):
@aetest.test
def test(self):
self.failed()
class TestcaseTwo(aetest.Testcase):
@aetest.test
def test(self):
self.failed()
class TestcaseThree(aetest.Testcase):
pass
class CommonCleanup(aetest.CommonCleanup):
pass
# run it
aetest.main(max_failures = 1)
# output result
#
# Max failure reached: aborting script execution
#
# SECTIONS/TESTCASES RESULT
# --------------------------------------------------------------------------
# .
# |-- TestcaseOne FAILED
# |-- TestcaseTwo BLOCKED
# |-- TestcaseThree BLOCKED
# `-- common_cleanup PASSED
As demonstrated above, when the total number of testcases failed reaches the given value, the testscript is aborted with the remaining testcases blocked.
Goto¶
Goto is the concept found in many programming languages that allows a one-way transfer of control to another line of code. To this day, there are still considerable debates with the academia and industry on its merits.
aetest
supports the concept of goto on the simple basis that testscripts,
while still a piece of software, is mostly linear in execution & logic. Adding
to it the support for goto grants users a higher order of control, and enables
early flow termination: exiting out of current section and skipping ahead
for the sole purpose of saving execution/testbed time. This was born out of pure
necessity: router testing is a heavily time-consuming process, and any time
saving measures is a plus.
In aetest
, goto jumps can be invoked as optional arguments to
Result APIs. This keyword-only argument accepts a list of targets
to jump to. Upon activation:
the test engine jumps to and executes each listed target location in sequence.
any by-passed sections receive a result based on the following criterion:
if the section that initiated the jump has a result of
Passed
, any by-passed section receives resultSkipped
: eg, they were skipped over intentionally.if the section that initiated the jump has a result other than
Passed
, any by-passed section receives resultBlocked
: eg, they were blocked due to “x” reasons.after the targets list is exhausted, execution continues as normally expected.
Important
aetest
only allows forward jumping. All goto targets must be valid
and in-line of current execution.
Target |
Comments |
---|---|
|
jumps to the testcase cleanup section, by-passing all other test sections |
|
jumps to the next testcase in line |
|
jumps to the script’s |
|
terminates the testscript immediately without going further. |
If a goto target is invalid or does not exist, the test engine gives the
current section a result of Errored
and continues executing. If there are no
more testcases, and goto next_tc
is called, no error is given and
common_cleanup
is executed to finalize the testscript.
This is by design: make sure your goto targets actually exist.
# Example
# -------
#
# goto syntax
# (pseudo code, not showing the entire testcase)
from pyats import aetest
# Syntax:
# <resultAPI>(goto = ['target_1', 'target_2', ... , 'target_x'])
class CommonSetup(aetest.CommonSetup):
@aetest.subsection
def subsection(self):
# goto with a message
self.errored('setup error, abandoning script', goto = ['exit'])
# --------------------------------------------------------------------------
class TestcaseOne(aetest.Testcase):
@aetest.setup
def setup(self):
# setup failed, go to cleanup of testcase
self.failed('test failed', goto = ['cleanup'])
# --------------------------------------------------------------------------
class TestcaseTwo(aetest.Testcase):
# test failed, move onto next testcase
@aetest.test
def test(self):
self.failed(goto = ['next_tc'])
# --------------------------------------------------------------------------
class TestcaseThree(aetest.Testcase):
@aetest.setup
def setup(self):
# setup failed, move onto cleanup of this testcase, then
# jump to common_cleanup directly.
self.failed(goto=['cleanup','common_cleanup'])
Essentially, goto is an optional step after providing a test section with an
appropriate result. For example, users may leverage this feature to skip to the
testcase’s cleanup
section after a dramatic failure in a test
section,
or skip all testcases directly and go to common_cleanup
instead. It allows
controlled skip-aheads during execution in favor of reducing script execution
time, avoiding expected errors/failures, etc.
# Example
# -------
#
# goto testscript demonstration
from pyats import aetest
# this testcase defines two tests
# in the first test, we'll give it a result and
# immediately skip forward to cleanup
class Testcase(aetest.Testcase):
@aetest.test
def test_one(self):
# skip ahead and go to cleanup instead
self.passed(goto = ['cleanup'])
@aetest.test
def test_two(self):
pass
@aetest.cleanup
def cleanup(self):
pass
# output result
#
# SECTIONS/TESTCASES RESULT
# --------------------------------------------------------------------------
# .
# `-- Testcase PASSED
# |-- test_one PASSED
# |-- test_two SKIPPED
# `-- cleanup PASSED
Custom Discovery and Order¶
Normally, testcases are discovered by the default discovery class,
ScriptDiscovery
which is located under discovery
module of aetest
package. This class implements the default behavior of testcase discovery and
ordering discussed throughout this user guide. For example, common_setup
has
to be run first, and then other testcases. Finally common_cleanup
runs as
the last testcase of the script.
Advanced users may want to extend and enhance this built-in discovery mechanism, eg, dynamic testcase/section generation, and/or alternative testcase ordering.
Custom Discovery¶
Users may overload discovery behavior at the following levels:
Script Discovery
Testcase Discovery
Common Discovery
Note
ScriptDiscovery finds testcases
within a testscript
.
TestcaseDiscovery finds testsections
within a testcase
CommonDiscovery finds subsections
within a common section
Rules:
pyats.aetest.discovery.ScriptDiscovery is the default discovery class for testcases and, it can be changed as shown in the following example:
from pyats.aetest import runtime
runtime.discoverer.script = MyCustomScriptDiscovery
Note
User defined custom script discovery class has to be inherited from
ScriptDiscovery
from pyats.aetest import runtime
from pyats.aetest.discovery import ScriptDiscovery
class MyCustomScriptDiscovery(ScriptDiscovery)
pass
runtime.discoverer.script = MyCustomScriptDiscovery
pyats.aetest.discovery.TestcaseDiscovery is the default discovery class for testsections and, it can be changed as shown in the following example:
from pyats.aetest import runtime
runtime.discoverer.testcase = MyCustomTestcaseDiscovery
Note
User defined custom test discovery class has to be inherited from
TestcaseDiscovery
from pyats.aetest import runtime
from pyats.aetest.discovery import TestcaseDiscovery
class MyCustomTestcaseDiscovery(TestcaseDiscovery)
pass
runtime.discoverer.testcase = MyCustomTestcaseDiscovery
Using the
runtime.discoverer
properties sets the default discovery classes.As shown in the following example, It is possible to provide different discovery classes to different testcases by using the
discoverer
attribute.
from pyats import aetest
from pyats.aetest import runtime
runtime.discoverer.testcase = MyDefaultDiscovery
class my_testcase1(aetest.Testcase):
discoverer = MyTestcaseDiscovery1
class my_testcase2(aetest.Testcase):
discoverer = MyTestcaseDiscovery2
class my_testcase3(aetest.Testcase):
pass
In the example above, my_testcase1
uses MyTestcaseDiscovery1
class,
my_testcase2
uses MyTestcaseDiscovery2
and my_testcase3
uses
MyDefaultDiscovery
class
pyats.aetest.discovery.CommonDiscovery is the default discovery class for subsections and, it can be changed as shown in the following example:
from pyats.aetest import runtime
runtime.discoverer.common = MyCustomCommonDiscovery
Note
User defined custom common test discovery class has to be inherited from
CommonDiscovery
from pyats.aetest import runtime
from pyats.aetest.discovery import CommonDiscovery
class MyCustomCommonDiscovery(CommonDiscovery)
pass
runtime.discoverer.common = MyCustomCommonDiscovery
Using the
runtime.discoverer
properties sets the default discovery classes.As shown in the following example, It is possible to provide different discovery classes to different common sections by using the
discoverer
attribute.
from pyats import aetest
from pyats.aetest import runtime
runtime.discoverer.common = MyDefaultDiscovery
class common_setup(aetest.CommonSetup):
pass
class common_cleanup(aetest.CommonCleanup):
discoverer = MyCommonDiscovery1
In the example above, common_cleanup
uses MyCommonDiscovery1
class,
and common_setup
uses MyDefaultDiscovery
class
How do discovery classes work?¶
There are 3 main methods that users need to know.
discover()
order()
__iter__()
Rules:
discover()
method “discoveres” all child sections defined in the current target
order()
method re-orders all sections according to rules of ordering and execution.
discover()
always callsorder()
before it returns valueFinally, the result comes from the order is passed to the execution engine
Custom script discovery classes must be inherited from ScriptDiscovery
Custom testcase discovery classes must be inherited from TestcaseDiscovery
Custom common subsection discovery classes must be inherited from CommonDiscovery
Custom methods must follow the same structure with the default discovery methods
__init__
method has to accept a parameter calledtarget
for all classes, containing the object to be discovered
__iter__
method instantiates all child sections defined in the current target and makes discovery class instance iterable.
ScriptDiscovery Implementation
class ScriptDiscovery():
def __init__(self, target):
self.target = target
def __iter__(self):
# some default logic written here
...
for testcase in self.discover():
yeild instantiated_testcase
def discover(self):
# some default logic written here
...
return self.order(setup, testcases, cleanup)
def order(self, setup=None, testcases=list(), cleanup=None):
# some default logic written here
...
return ordered_testcases
TestcaseDiscovery Implementation
class TestcaseDiscovery():
def __init__(self, target):
self.target = target
def __iter__(self):
# some default logic written here
...
for test in self.discover():
yeild instantiated_test
def discover(self):
# some default logic written here
...
return self.order(setup, testcases, cleanup)
def order(self, setup=None, tests=list(), cleanup=None):
# some default logic written here
...
return ordered_tests
CommonDiscovery Implementation
class CommonDiscovery():
def __init__(self, target):
self.target = target
def __iter__(self):
# some default logic written here
...
for test in self.discover():
yeild instantiated_test
def discover(self):
# some default logic written here
...
return self.order(tests)
def order(self, *sections):
# some default logic written here
...
return ordered_tests
Custom Discovery Class Examples
class MyCustomScriptDiscovery(ScriptDiscovery):
def discover(self):
# some logic
return [self.target.module.tc_one, self.target.module.common_cleanup]
class MyCustomTestcaseDiscovery(TestcaseDiscovery):
def discover(self):
#some logic
return [self.target.my_section1]
class MyCustomCommonDiscovery(CommonDiscovery):
def discover(self):
# some logic
return [self.target.subsection1]
Custom Ordering¶
There are 3 levels of custom ordering just like in the custom discovery,
Script Level
Testcase level
Common Level
In order to use Script level ordering a custom discovery class has to be
provided to the runtime.discoverer.script
of aetest
. For more
information on how to provide a custom ScriptDiscovery class to the
runtime.discoverer.script
please refer Custom Discovery
Rules:
Providing
order
method will replace the default ordering logic with the user defined one
class MyCustomScriptDiscovery(ScriptDiscovery):
def order(self, setup=None, testcases=list(), cleanup=None):
# some logic
return [\
self.module.common_setup,\
self.module.tc_two,\
self.module.tc_one,\
self.module.common_cleanup]
Rules:
The rules that applies to the
discover
method also applies fororder
method as well.
discoverer
attribute can be used in order to definetestcase
specific discovery classes for testsections and subsections.For more information about how to provide discovery classes, please check Custom Discovery
class MyCustomTestcaseDiscovery(TestcaseDiscovery):
def order(self, setup=None, tests=list(), cleanup=None):
# some logic
return [\
self.target.common_setup,\
self.target.tc_two,\
self.target.tc_one,\
self.target.common_cleanup]
class MyCustomCommonDiscovery(CommonDiscovery):
def order(self, *sections):
# some logic
return [\
self.target.subsection1,\
self.target.subsection2]
Example¶
Demonstration of how to provide custom Script, Testcase and Common Discovery classes and how they work
from pyats import aetest
# importing the runtime to have access to the runtime.discoverer
from pyats.aetest import runtime
# importing ScriptDiscovery, TestcaseDiscovery and CommonDiscovery classes
# for inheritance
from pyats.aetest.discovery import ScriptDiscovery, TestcaseDiscovery,\
CommonDiscovery
# Custom Script Discovery class that changes only the discover method
# which will return common_setup and tc_one
class CustomScriptDiscovery(ScriptDiscovery):
def discover(self):
return [self.target.module.common_setup, self.target.module.tc_one]
# Custom Testcase Discovery Class that changes both discover and order
# classes. This class returns nothing from the discover method to the order
# so, nothing will run in the provided tetscase
class CustomTestcaseDiscovery_1(TestcaseDiscovery):
def discover(self):
return []
def order(self, setup=None, testcases=list(), cleanup=None):
if setup is not None:
testcases.insert(0, setup)
if cleanup is not None:
testcases.append(cleanup)
return testcases
# Common Discovery Class that returns nothing to be run
class CustomCommonDiscovery_1(CommonDiscovery):
def discover(self):
return []
def order(self, *sections):
return sections
# Custom Common Discovery Class, order method returns sample_subsection_1
# so it's the only test section that will run for the provided testcase
class CustomCommonDiscovery_2(CommonDiscovery):
def order(self, *sections):
return [self.target.sample_subsection_1]
# setting runtime.discoverer properties so that default discovery classes
# will change to user defined ones.
runtime.discoverer.script = CustomScriptDiscovery
runtime.discoverer.testcase = CustomTestcaseDiscovery_1
runtime.discoverer.common = CustomCommonDiscovery_1
# This testcase has discoverer attribte so that, runtime.discoverer.common
# will be ignored
class common_setup(aetest.CommonSetup):
discoverer = CustomCommonDiscovery_2
@aetest.subsection
def sample_subsection_1(self):
pass
@aetest.subsection
def sample_subsection_2(self, section):
pass
class tc_one(aetest.Testcase):
@aetest.setup
def prepare_testcase(self, section):
pass
@ aetest.test
def simple_test_1(self):
pass
@ aetest.test
def simple_test_2(self):
pass
@aetest.cleanup
def clean_testcase(self):
pass
class common_cleanup(aetest.CommonCleanup):
@aetest.subsection
def clean_everything(self):
pass
In this example, CustomScriptDiscovery
class was passed as the default
discovery class to the runtime.discoverer.script
. It only has discover
method and this method returns only common_setup
and tc_one
. Therefore,
only these 2 testcases runs.
CustomTestDiscovery_1
class was also provided as default testcase discovery
class so all the testcases within this script is going to use this class unless
there is discoverer
parameter is provided. Nothing should be run as it
doesn’t return any test section.
CustomCommonDiscovery_1
class was also provided as default common discovery
class so all the common sections within this script is going to use this class
unless there is discoverer
parameter is provided like in common_setup
.
The output of order
and discover
calls from this class will return empty
list. So, none of the testcases, should run any testsection except
common_setup
discoverer
was provided within the common_setup
section so, this
parameter will be applied over runtime.discoverer.commmon
for this section.
CustomCommonDiscovery_2
class was used and it returns only
sample_subsection_1
.
Output
%EASYPY-INFO: +------------------------------------------------------------------------------+
%EASYPY-INFO: | Task Result Details |
%EASYPY-INFO: +------------------------------------------------------------------------------+
%EASYPY-INFO: Task-1: basic_example_script
%EASYPY-INFO: |-- commonSetup PASSED
%EASYPY-INFO: | `-- sample_subsection_1 PASSED
%EASYPY-INFO: `-- tc_one PASSED