Tutorials / Framework Features
Multi-layer parametrization
Parametrize at the suite level and the test level independently. Write one test method, run it across every combination of environments, browsers, or user roles — and get per-variant tracking, retries, and reports for free.
Suite-level parameters
parameters= on @Suite runs the entire suite once per entry. The current entry is injected into any lifecycle method or test that declares suite_parameter as an argument:
@Suite(parameters=[ {"env": "staging"}, {"env": "prod"}, ]) class CheckoutSuite: @beforeClass() def setup(self, suite_parameter): self.base_url = suite_parameter["env"] @test() def test_add_to_cart(self, suite_parameter): # suite_parameter is {"env": "staging"} on first run # suite_parameter is {"env": "prod"} on second run checkout(self.base_url, suite_parameter["env"])
Test-level parameters
parameters= on @test runs that test once per entry within each suite run. The current entry is injected as parameter:
@Suite() class CheckoutSuite: @test(parameters=[ {"browser": "chrome"}, {"browser": "firefox"}, {"browser": "safari"}, ]) def test_checkout(self, parameter): browser = parameter["browser"] driver = new_driver(browser) ...
Both layers together: cross-product coverage
When both suite and test have parameters=, Test Junkie runs every combination. Two environments × three browsers gives six independent variants from a single method:
@Suite(parameters=[ {"env": "staging"}, {"env": "prod"}, ]) class CheckoutSuite: @beforeClass() def setup(self, suite_parameter): self.base_url = suite_parameter["env"] @test(parameters=[ {"browser": "chrome"}, {"browser": "firefox"}, {"browser": "safari"}, ]) def test_checkout(self, parameter, suite_parameter): browser = parameter["browser"] env = suite_parameter["env"] driver = new_driver(browser) checkout(driver, env) # 6 variants: staging/chrome, staging/firefox, # staging/safari, prod/chrome, prod/firefox, prod/safari
parameter and/or suite_parameter in its signature and injects the current values. These names are fixed — use parameter["browser"] to get the browser value, not browser directly.Opting in: signature-based injection
Parameter injection is opt-in via the function signature — for every function in the suite, not just @test. This applies equally to @beforeClass, @afterClass, @beforeTest, and @afterTest:
-
Lifecycle functions (
@beforeClass,@afterClass,@beforeTest,@afterTest) — declaresuite_parameterto receive the current suite parameter. They do not have access toparameter(test-level). -
Test functions (
@test) — declareparameter,suite_parameter, or both. A test withoutsuite_parameterruns exactly once regardless of how many suite parameters are defined.
@Suite(parameters=[ {"env": "staging"}, {"env": "prod"}, ]) class CheckoutSuite: @beforeClass() def setup(self, suite_parameter): # suite_parameter declared: runs twice (staging, prod) self.base_url = suite_parameter["env"] @afterClass() def teardown(self): # no suite_parameter: still runs twice (once per suite run) Browser.shutdown() @beforeTest() def before_each(self, suite_parameter): # suite_parameter declared: receives current env before each test log(f"starting test on {suite_parameter['env']}") @test() def test_homepage(self): # no suite_parameter: runs once total, not twice assert self.base_url is not None @test() def test_checkout(self, suite_parameter): # suite_parameter declared: runs twice (staging + prod) checkout(self.base_url, suite_parameter["env"])
@beforeClass and @afterClass run once per suite-parameter iteration regardless of whether they declare suite_parameter — they live outside the test loop. @beforeTest and @afterTest follow the test they wrap: if the test is skipped for a suite-parameter iteration (because it doesn't declare suite_parameter), those hooks are skipped too. For @test, the declaration controls both whether the value is injected and whether the test participates in the cross-product at all.Per-variant tracking and retries
Each variant has its own metrics entry. If test_checkout[prod][safari] fails, it fails independently of test_checkout[staging][chrome]. Retries apply per variant, not per method:
@test( retry=2, retry_on=[ConnectionError], parameters=[ {"browser": "chrome"}, {"browser": "safari"}, ] ) def test_checkout(self, parameter, suite_parameter): ... # if safari variant gets a ConnectionError, only it retries # chrome variant result is unaffected