### Molotov Full Example Source: https://molotov.readthedocs.io/en/stable/_sources/fixtures.rst.txt A comprehensive example demonstrating the order of calls for Molotov's test lifecycle decorators, including global setup, session setup, scenario execution, session teardown, and global teardown. ```python import molotov @molotov.global_setup() def global_setup_func(): print('Global setup') @molotov.setup() def setup_func(): print('Setup') @molotov.setup_session() async def setup_session_func(): print('Setup session') @molotov.teardown_session() async def teardown_session_func(): print('Teardown session') @molotov.teardown() def teardown_func(): print('Teardown') @molotov.global_teardown() def global_teardown_func(): print('Global teardown') @molotov.scenario(weight=1) async def scenario_one(request): print('Scenario one') @molotov.scenario(weight=1) async def scenario_two(request): print('Scenario two') ``` -------------------------------- ### Configure Test Fixtures with global_setup and setup Source: https://molotov.readthedocs.io/en/stable/tutorial Use `global_setup()` to run a function once before tests start, typically for initializing global configurations like headers. Use `setup()` to configure the session object for each worker, allowing shared data to be passed. ```python from molotov import setup, global_setup, scenario _HEADERS = {} @global_setup() def init_test(args): _HEADERS['Authorization'] = 'Token xxxx' @setup() async def init_worker(worker_id, args): return {'headers': _HEADERS} ``` -------------------------------- ### Worker Setup Decorator Source: https://molotov.readthedocs.io/en/stable/fixtures The setup() decorator registers a function that is called once for each worker when it starts up. It can return a dictionary to configure the aiohttp.ClientSession. ```APIDOC ## molotov.setup ### Description Decorator to register a function that runs once per worker startup. ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Arguments - **worker_id** (integer) - The ID of the current worker. - **args** (list) - Arguments used to start Molotov. ### Notes - The decorated function can return a dictionary. This dictionary will be passed as keywords to the `aiohttp.ClientSession` constructor. - Useful for setting session-wide options like Authorization headers. - The decorated function should be a coroutine. ### Request Example ```python import molotov @molotov.setup() async def init_worker(worker_num, args): headers = {"Authorization": "Bearer my_token"} return {"headers": headers} ``` ### Response None directly from the decorator. The decorated function returns a dictionary that is used to initialize the `aiohttp.ClientSession`. ``` -------------------------------- ### Define a complete Molotov test script Source: https://molotov.readthedocs.io/en/stable/fixtures A comprehensive example demonstrating global setup, worker initialization, session configuration, scenario execution, and teardown procedures. ```python """ This Molotov script has: - a global setup fixture that sets variables - an init worker fixture that sets the session headers - an init session that attachs an object to the current session - 1 scenario - 2 tear downs fixtures """ import molotov class SomeObject(object): """Does something smart in real life with the async loop.""" def __init__(self, loop): self.loop = loop def cleanup(self): pass @molotov.global_setup() def init_test(args): molotov.set_var("SomeHeader", "1") molotov.set_var("endpoint", "http://localhost:8080") @molotov.setup() async def init_worker(worker_num, args): headers = {"AnotherHeader": "1", "SomeHeader": molotov.get_var("SomeHeader")} return {"headers": headers} @molotov.setup_session() async def init_session(worker_num, session): molotov.get_context(session).attach("ob", SomeObject(loop=session.loop)) @molotov.scenario(100) async def scenario_one(session): endpoint = molotov.get_var("endpoint") async with session.get(endpoint) as resp: res = await resp.json() assert res["result"] == "OK" assert resp.status == 200 @molotov.teardown_session() async def end_session(worker_num, session): molotov.get_context(session).ob.cleanup() @molotov.teardown() def end_worker(worker_num): print("This is the end for %d" % worker_num) @molotov.global_teardown() def end_test(): ``` -------------------------------- ### Implement Fixtures and Session Management Source: https://molotov.readthedocs.io/en/stable/examples Demonstrates global setup, worker initialization, session attachment, and teardown hooks. ```python import molotov class SomeObject(object): """Does something smart in real life with the async loop.""" def __init__(self, loop): self.loop = loop def cleanup(self): pass @molotov.global_setup() def init_test(args): molotov.set_var("SomeHeader", "1") molotov.set_var("endpoint", "http://localhost:8080") @molotov.setup() async def init_worker(worker_num, args): headers = {"AnotherHeader": "1", "SomeHeader": molotov.get_var("SomeHeader")} return {"headers": headers} @molotov.setup_session() async def init_session(worker_num, session): molotov.get_context(session).attach("ob", SomeObject(loop=session.loop)) @molotov.scenario(100) async def scenario_one(session): endpoint = molotov.get_var("endpoint") async with session.get(endpoint) as resp: res = await resp.json() assert res["result"] == "OK" assert resp.status == 200 @molotov.teardown_session() async def end_session(worker_num, session): molotov.get_context(session).ob.cleanup() @molotov.teardown() def end_worker(worker_num): print("This is the end for %d" % worker_num) @molotov.global_teardown() def end_test(): print("This is the end of the test.") ``` -------------------------------- ### Implement test fixtures Source: https://molotov.readthedocs.io/en/stable/_sources/tutorial.rst.txt Use global_setup and setup decorators to configure shared headers or session state. ```python from molotov import setup, global_setup, scenario _HEADERS = {} @global_setup() def init_test(args): _HEADERS['Authorization'] = 'Token xxxx' @setup() async def init_worker(worker_id, args): return {'headers': _HEADERS} ``` -------------------------------- ### Molotov Script with Fixtures and Scenarios Source: https://molotov.readthedocs.io/en/stable/examples This script demonstrates a comprehensive Molotov setup including global setup and teardown fixtures, worker-specific setup and teardown, and multiple scenarios. It's useful for complex load testing scenarios requiring pre-test configurations and post-test cleanup. ```python import json from molotov import ( scenario, setup, global_setup, global_teardown, teardown, get_context, ) _API = "http://localhost:8080" _HEADERS = {} # notice that the global setup, global teardown and teardown # are not a coroutine. @global_setup() def init_test(args): _HEADERS["SomeHeader"] = "1" @global_teardown() def end_test(): print("This is the end") @setup() async def init_worker(worker_num, args): headers = {"AnotherHeader": "1"} headers.update(_HEADERS) return {"headers": headers} @teardown() def end_worker(worker_num): print("This is the end for %d" % worker_num) # @scenario(weight=40) async def scenario_one(session): async with session.get(_API) as resp: if get_context(session).statsd: get_context(session).statsd.incr("BLEH") res = await resp.json() assert res["result"] == "OK" assert resp.status == 200 @scenario(weight=30) async def scenario_two(session): async with session.get(_API) as resp: assert resp.status == 200 ``` -------------------------------- ### Build and verify the environment Source: https://molotov.readthedocs.io/en/stable/_sources/tutorial.rst.txt Commands to build the virtual environment and verify the Molotov installation. ```bash $ cd /tmp/mytest $ make build $ source venv/bin/activate (venv) ``` ```bash (venv) $ molotov --version 2.6 ``` -------------------------------- ### Molotov Extension Output Example Source: https://molotov.readthedocs.io/en/stable/_sources/extending.rst.txt Example output from a Molotov load test that uses an extension to report the average response time. The output includes Molotov's version, test status, and summary statistics. ```text Loading extension '../molotov/tests/example6.py' Preparing 1 worker... OK [W:0] Starting [W:0] Setting up session [W:0] Running scenarios Average response time 16ms **** Molotov v2.6. Happy breaking! **** SUCCESSES: 10 | FAILURES: 0 *** Bye *** ``` -------------------------------- ### Verify Molotov Installation Source: https://molotov.readthedocs.io/en/stable/tutorial Check if Molotov is installed correctly by running `molotov --version` within the activated virtual environment. ```bash (venv) $ molotov --version 2.6 ``` -------------------------------- ### Build Molotov Test Environment Source: https://molotov.readthedocs.io/en/stable/tutorial After initializing a project, use `make build` to create a virtual environment and install Molotov. Activate the environment using `source venv/bin/activate`. ```bash $ cd /tmp/mytest $ make build $ source venv/bin/activate (venv) ``` -------------------------------- ### Molotov Test Examples Source: https://molotov.readthedocs.io/en/stable/_sources/examples.rst.txt A collection of example scripts used for testing the Molotov framework. ```python import molotov @molotov.scenario(weight=10) async def scenario_one(session): async with session.get('http://localhost:8080') as resp: assert resp.status == 200 @molotov.scenario(weight=90) async def scenario_two(session): async with session.get('http://localhost:8080/proc') as resp: assert resp.status == 200 ``` ```python import molotov @molotov.scenario(weight=10) async def scenario_one(session): async with session.get('http://localhost:8080') as resp: assert resp.status == 200 @molotov.scenario(weight=90) async def scenario_two(session): async with session.get('http://localhost:8080/proc') as resp: assert resp.status == 200 @molotov.setup() def init(worker_num, args): print("SETUP") @molotov.teardown() def finish(worker_num, args): print("TEARDOWN") ``` ```python import molotov @molotov.scenario(weight=10) async def scenario_one(session): async with session.get('http://localhost:8080') as resp: assert resp.status == 200 @molotov.scenario(weight=90) async def scenario_two(session): async with session.get('http://localhost:8080/proc') as resp: assert resp.status == 200 @molotov.setup() def init(worker_num, args): return {'some': 'data'} @molotov.teardown() def finish(worker_num, args): pass ``` ```python import molotov @molotov.scenario(weight=10) async def scenario_one(session): async with session.get('http://localhost:8080') as resp: assert resp.status == 200 @molotov.scenario(weight=90) async def scenario_two(session): async with session.get('http://localhost:8080/proc') as resp: assert resp.status == 200 @molotov.setup() def init(worker_num, args): return {'some': 'data'} @molotov.teardown() def finish(worker_num, args): pass @molotov.setup_session() async def init_session(worker_num, session): session.headers.update({'X-Test': '1'}) @molotov.teardown_session() async def finish_session(worker_num, session): pass ``` ```python import molotov @molotov.scenario(weight=10) async def scenario_one(session): async with session.get('http://localhost:8080') as resp: assert resp.status == 200 @molotov.scenario(weight=90) async def scenario_two(session): async with session.get('http://localhost:8080/proc') as resp: assert resp.status == 200 @molotov.setup() def init(worker_num, args): return {'some': 'data'} @molotov.teardown() def finish(worker_num, args): pass @molotov.setup_session() async def init_session(worker_num, session): session.headers.update({'X-Test': '1'}) @molotov.teardown_session() async def finish_session(worker_num, session): pass @molotov.events() async def print_event(event, **info): if event == 'scenario_start': print("Starting %s" % info['name']) elif event == 'scenario_success': print("Success!") ``` ```python import molotov @molotov.scenario(weight=10) async def scenario_one(session): async with session.get('http://localhost:8080') as resp: assert resp.status == 200 @molotov.scenario(weight=90) async def scenario_two(session): async with session.get('http://localhost:8080/proc') as resp: assert resp.status == 200 @molotov.setup() def init(worker_num, args): return {'some': 'data'} @molotov.teardown() def finish(worker_num, args): pass @molotov.setup_session() async def init_session(worker_num, session): session.headers.update({'X-Test': '1'}) @molotov.teardown_session() async def finish_session(worker_num, session): pass @molotov.events() async def print_event(event, **info): if event == 'scenario_start': print("Starting %s" % info['name']) elif event == 'scenario_success': print("Success!") @molotov.global_setup() def global_init(args): print("GLOBAL SETUP") @molotov.global_teardown() def global_finish(args): print("GLOBAL TEARDOWN") ``` ```python import molotov @molotov.scenario(weight=10) async def scenario_one(session): async with session.get('http://localhost:8080') as resp: assert resp.status == 200 @molotov.scenario(weight=90) async def scenario_two(session): async with session.get('http://localhost:8080/proc') as resp: assert resp.status == 200 @molotov.setup() def init(worker_num, args): return {'some': 'data'} @molotov.teardown() def finish(worker_num, args): pass @molotov.setup_session() async def init_session(worker_num, session): session.headers.update({'X-Test': '1'}) @molotov.teardown_session() async def finish_session(worker_num, session): pass @molotov.events() async def print_event(event, **info): if event == 'scenario_start': print("Starting %s" % info['name']) elif event == 'scenario_success': print("Success!") @molotov.global_setup() def global_init(args): print("GLOBAL SETUP") @molotov.global_teardown() def global_finish(args): print("GLOBAL TEARDOWN") @molotov.scenario(weight=10) async def scenario_three(session): async with session.get('http://localhost:8080/fail') as resp: assert resp.status == 500 ``` -------------------------------- ### Global Setup Decorator Source: https://molotov.readthedocs.io/en/stable/fixtures The global_setup() decorator marks a function to be called once at the very beginning of the test run, before any processes or workers are created. ```APIDOC ## molotov.global_setup ### Description Decorator to register a function that runs once when the test starts. ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Arguments - **args** (list) - Arguments used to start Molotov. ### Notes - The decorated function is called before processes and workers are created. - Useful for setting up shared fixtures. - The decorated function should not be a coroutine. ### Request Example ```python import molotov @molotov.global_setup() def init_test(args): molotov.set_var("shared_data", "initial_value") print("Global setup complete.") ``` ### Response None directly from the decorator. The decorated function can use `molotov.set_var()` to store data. ``` -------------------------------- ### Install Molotov via pip Source: https://molotov.readthedocs.io/en/stable/_sources/installation.rst.txt Use this command to install the Molotov package in your Python environment. ```bash $ pip install molotov ``` -------------------------------- ### Synchronous Request Helpers Source: https://molotov.readthedocs.io/en/stable/helpers Utilities for performing synchronous HTTP requests during test setup using aiohttp. ```APIDOC ## molotov.request(endpoint, verb='GET', session_options=None, **options) ### Description Performs a synchronous request using a dedicated event loop and aiohttp.ClientSession. ### Parameters - **endpoint** (string) - Required - The URL to call. - **verb** (string) - Optional - The HTTP method (default: GET). - **session_options** (dict) - Optional - Options to initialize the session. - **options** (dict) - Optional - Extra options for the request. ### Response - **content** (any) - The response body. - **status** (int) - The HTTP status code. - **headers** (dict) - The response headers. ## molotov.json_request(endpoint, verb='GET', session_options=None, **options) ### Description Performs a synchronous request and automatically extracts JSON content from the response. ``` -------------------------------- ### Session Setup Decorator Source: https://molotov.readthedocs.io/en/stable/fixtures The setup_session() decorator registers a coroutine that is called once per worker when the aiohttp.ClientSession is created. It allows attaching custom objects or performing session-specific initializations. ```APIDOC ## molotov.setup_session ### Description Decorator to register a coroutine that runs once per worker when the session is created. ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Arguments - **worker_id** (integer) - The ID of the current worker. - **session** (aiohttp.ClientSession) - The `aiohttp.ClientSession` instance that was created. ### Notes - Allows attaching extra attributes to the session. - Can use `session.loop` to interact with the event loop. - A good place to attach objects that interact with the event loop. - The decorated function should be a coroutine. ### Request Example ```python import molotov class MySessionHelper: def __init__(self, loop): self.loop = loop @molotov.setup_session() async def init_session(worker_num, session): helper = MySessionHelper(loop=session.loop) molotov.get_context(session).attach("helper", helper) ``` ### Response None directly from the decorator. The decorated function can modify the session or attach data to its context. ``` -------------------------------- ### Define a basic load test scenario Source: https://molotov.readthedocs.io/en/stable/_sources/tutorial.rst.txt Create a simple scenario using the @scenario decorator to perform an HTTP GET request. ```python from molotov import scenario @scenario(weight=100) async def _test(session): async with session.get('https://example.com') as resp: assert resp.status == 200, resp.status ``` -------------------------------- ### Synchronous Requests Source: https://molotov.readthedocs.io/en/stable/_sources/helpers.rst.txt Utility functions to perform synchronous HTTP requests, typically used within setup functions. ```APIDOC ## Synchronous Request Functions ### Description Perform synchronous HTTP requests during the setup phase of your tests. ### Functions - **molotov.request(url, ...)**: Performs a synchronous HTTP request. - **molotov.json_request(url, ...)**: Performs a synchronous HTTP request and parses the JSON response. ### Request Example ```python from molotov import global_setup, json_request, set_var @global_setup(args) def _setup(): set_var('token', json_request('http://example.com')['content']) ``` ``` -------------------------------- ### Run Molotov Test for a Duration Source: https://molotov.readthedocs.io/en/stable/tutorial Run the load test for a specified duration (e.g., 30 seconds) using the `-d` flag. This command starts the interactive console UI. ```bash (venv) $ molotov -d 30 -x loadtest.py ``` -------------------------------- ### Define a Molotov Scenario Source: https://molotov.readthedocs.io/en/stable/events This scenario uses `molotov.scenario(100)` to define a test case that makes a GET request to `http://localhost:8080`, parses the JSON response, and asserts the result and status code. ```python @molotov.scenario(100) async def scenario_one(session): async with session.get("http://localhost:8080") as resp: res = await resp.json() assert res["result"] == "OK" assert resp.status == 200 ``` -------------------------------- ### Set and Get Global Variables in Molotov Source: https://molotov.readthedocs.io/en/stable/_sources/helpers.rst.txt Use set_var and get_var to store and retrieve objects across different test fixtures or tests. This is useful for sharing state, such as authentication tokens. ```python from molotov import global_setup, json_request, set_var @global_setup(args) def _setup(): set_var('token', json_request('http://example.com')['content']) ``` -------------------------------- ### Get Global Variable in Molotov Source: https://molotov.readthedocs.io/en/stable/helpers Use get_var to retrieve a globally stored variable by its name. If the variable is not set, it can be initialized using a provided factory callable. ```python molotov.get_var(_name_ , _factory=None_) ``` -------------------------------- ### Initialize a Molotov project Source: https://molotov.readthedocs.io/en/stable/_sources/tutorial.rst.txt Use the molostart command to generate the default project structure. ```bash $ molostart **** Molotov Quickstart **** Answer to a few questions to get started... > Target directory [.]: /tmp/mytest > Create Makefile [y]: Generating Molotov test... …copying 'Makefile' in '/tmp/mytest' …copying 'loadtest.py' in '/tmp/mytest' …copying 'molotov.json' in '/tmp/mytest' All done. Happy Breaking! Go in '/tmp/mytest' Run 'make build' to get started... ``` -------------------------------- ### Run Molotov from GitHub Source: https://molotov.readthedocs.io/en/stable/_sources/slave.rst.txt Execute Molotov tests directly from a GitHub repository using the 'moloslave' command. This command uses the configuration defined in the repository's molotov.json file. ```bash $ moloslave https://github.com/tarekziade/molotov test ``` -------------------------------- ### Run Molotov with Sizing Source: https://molotov.readthedocs.io/en/stable/_sources/tutorial.rst.txt Execute a load test with automatic sizing enabled using the --sizing flag. ```bash (venv) $ molotov --sizing loadtest.py ``` -------------------------------- ### Molotov Configuration File Source: https://molotov.readthedocs.io/en/stable/_sources/slave.rst.txt This is the molotov.json configuration file. It defines a list of tests to run, each with a name and command-line options. ```json { "test": { "--delay": 10, "--timeout": 10 }, "big": { "--delay": 10, "--timeout": 10, "--users": 100 }, "scenario_two_once": { "--delay": 10, "--timeout": 10, "--users": 10, "--step-users": 5, "--step-delay": 1 } } ``` -------------------------------- ### Execute load tests via CLI Source: https://molotov.readthedocs.io/en/stable/_sources/tutorial.rst.txt Commands to run tests in single-run mode, console UI mode, and with varying concurrency levels. ```bash (venv) $ molotov --single-run -c loadtest.py **** Molotov v2.0. Happy breaking! **** Preparing 1 workers...OK SUCCESSES: 1 | FAILURES: 0 | WORKERS: 1 *** Bye *** ``` ```bash (venv) $ molotov -d 30 -x loadtest.py ``` ```bash (venv) $ molotov -w 10 -d 30 -x loadtest.py ``` ```bash (venv) $ molotov -w 10 -p 4 -d 30 -x loadtest.py ``` -------------------------------- ### Define multiple scenarios with weights Source: https://molotov.readthedocs.io/en/stable/_sources/tutorial.rst.txt Configure multiple scenarios with specific weights to control execution frequency. ```python from molotov import scenario @scenario(weight=20) async def _test(session): async with session.get('https://example.com') as resp: assert resp.status == 200, resp.status @scenario(weight=20) async def _test2(session): # do something @scenario(weight=60) async def _test3(session): # do something different ``` -------------------------------- ### Run Molotov load test in Docker Source: https://molotov.readthedocs.io/en/stable/_sources/docker.rst.txt Executes a load test from a public repository using the Molotov Docker image. ```bash docker run -i --rm -e TEST_REPO=https://github.com/tarekziade/molotov -e TEST_NAME=test tarekziade/molotov:latest ``` -------------------------------- ### Registering Event Handlers with @molotov.events Source: https://molotov.readthedocs.io/en/stable/_sources/events.rst.txt Demonstrates how to use the @molotov.events decorator to listen for and print all available load test events. ```python import molotov @molotov.events() async def print_event(event, **kwargs): print(f"Event: {event}") print(f"Args: {kwargs}") @molotov.scenario(weight=10) async def scenario_one(session): async with session.get('http://localhost:8080') as resp: assert resp.status == 200 ``` -------------------------------- ### Run Molotov Docker Image Source: https://molotov.readthedocs.io/en/stable/docker Execute a load test using the molotov Docker image. Set TEST_REPO to the public Git repository containing your test and TEST_NAME to the specific test file to run. ```docker docker run -i --rm -e TEST_REPO=https://github.com/tarekziade/molotov -e TEST_NAME=test tarekziade/molotov:latest ``` -------------------------------- ### Define Basic Scenarios Source: https://molotov.readthedocs.io/en/stable/examples Basic structure for defining multiple scenarios with weights using the @scenario decorator. ```python # @scenario(weight=30) async def scenario_three(session): somedata = json.dumps({"OK": 1}) async with session.post(_API, data=somedata) as resp: assert resp.status == 200 ``` ```python from molotov import scenario _API = "http://localhost:8080" @scenario(weight=40) async def scenario_one(session): async with session.get(_API) as resp: res = await resp.json() assert res["result"] == "OK" assert resp.status == 200 @scenario(weight=60) async def scenario_two(session): async with session.get(_API) as resp: assert resp.status == 200 ``` -------------------------------- ### Execute Molotov with an extension Source: https://molotov.readthedocs.io/en/stable/extending Command-line usage of the --use-extension flag to load a custom script during a load test. ```bash $ molotov --use-extension molotov/tests/example6.py --max-runs 10 loadtest.py -c Loading extension '../molotov/tests/example6.py' Preparing 1 worker... OK [W:0] Starting [W:0] Setting up session [W:0] Running scenarios Average response time 16ms **** Molotov v2.6. Happy breaking! **** SUCCESSES: 10 | FAILURES: 0 *** Bye *** ``` -------------------------------- ### Configure molotov.json for GitHub execution Source: https://molotov.readthedocs.io/en/stable/slave Define test scenarios, environment variables, and requirements in a JSON file located at the root of the repository. ```json { "molotov": { "env": { "SERVER_URL": "http://aserver.net" }, "requirements": "requirements.txt", "tests": { "big": { "console": true, "duration": 10, "exception": true, "processes": 10, "scenario": "molotov/tests/example.py", "workers": 100 }, "fail": { "exception": true, "max_runs": 1, "scenario": "molotov/tests/example3.py" }, "scenario_two_once": { "console": true, "exception": true, "max_runs": 1, "scenario": "molotov/tests/example.py", "single_mode": "scenario_two" }, "test": { "console": true, "duration": 1, "exception": true, "verbose": 1, "console_update": 0, "scenario": "molotov/tests/example.py" } } } } ``` -------------------------------- ### Run Molotov Load Test Script Source: https://molotov.readthedocs.io/en/stable Execute a Molotov load test script from the command line. Specify the script path, number of processes (-p), workers (-w), and duration in seconds (-d). ```bash $ molotov molotov/tests/example.py -p 10 -w 200 -d 60 ``` -------------------------------- ### Running Molotov with an Extension Source: https://molotov.readthedocs.io/en/stable/_sources/extending.rst.txt Execute a Molotov load test using a custom Python extension. The --use-extension flag specifies the path to the extension module, and --max-runs limits the number of test iterations. ```bash $ molotov --use-extension molotov/tests/example6.py --max-runs 10 loadtest.py -c ``` -------------------------------- ### Define Load Test Scenarios in Python Source: https://molotov.readthedocs.io/en/stable Create Python coroutines decorated with @scenario to define load test functions. Each scenario receives an aiohttp.ClientSession object for making requests. Use assert statements to validate responses and status codes. ```python """ This Molotov script has 2 scenario """ from molotov import scenario _API = "http://localhost:8080" @scenario(weight=40) async def scenario_one(session): async with session.get(_API) as resp: res = await resp.json() assert res["result"] == "OK" assert resp.status == 200 @scenario(weight=60) async def scenario_two(session): async with session.get(_API) as resp: assert resp.status == 200 ``` -------------------------------- ### Run a Single Molotov Scenario Iteration Source: https://molotov.readthedocs.io/en/stable/tutorial Execute a load test scenario once using the `--single-run` and `-c` flags. This is useful for debugging and verifying a single scenario's behavior. ```bash (venv) $ molotov --single-run -c loadtest.py **** Molotov v2.0. Happy breaking! **** Preparing 1 workers...OK SUCCESSES: 1 | FAILURES: 0 | WORKERS: 1 *** Bye *** ``` -------------------------------- ### Implement custom response time tracking extension Source: https://molotov.readthedocs.io/en/stable/extending A Python module using @molotov.events and @molotov.global_teardown to calculate and display average request response times. ```python """ This Molotov script show how you can print the average response time. """ import molotov import time _T = {} def _now(): return time.time() * 1000 @molotov.events() async def record_time(event, **info): req = info.get("request") if event == "sending_request": _T[req] = _now() elif event == "response_received": _T[req] = _now() - _T[req] @molotov.global_teardown() def display_average(): average = sum(_T.values()) / len(_T) print("\nAverage response time %dms" % average) ``` -------------------------------- ### Molotov Decorators and Lifecycle Fixtures Source: https://molotov.readthedocs.io/en/stable/_sources/fixtures.rst.txt Overview of the primary decorators for scenario definition and the lifecycle hooks for test execution management. ```APIDOC ## Molotov API Reference ### Description Molotov provides decorators to define test scenarios and manage the lifecycle of test execution through various setup and teardown hooks. ### Decorators - **@scenario(weight=1)**: Marks a function as a test scenario. The weight parameter determines the frequency of execution. - **@scenario_picker**: Decorates a custom function to override the default random scenario selection logic. ### Lifecycle Fixtures - **@global_setup**: Runs once before any test execution. - **@setup**: Runs before each scenario execution. - **@setup_session**: Runs once when a session starts. - **@teardown_session**: Runs once when a session ends. - **@teardown**: Runs after each scenario execution. - **@global_teardown**: Runs once after all test execution is complete. ``` -------------------------------- ### Define a Basic Molotov Scenario Source: https://molotov.readthedocs.io/en/stable/tutorial Create a load test scenario by defining an asynchronous function decorated with `@scenario`. This function receives a `session` object for making HTTP requests and should include assertions to validate responses. ```python from molotov import scenario @scenario(weight=100) async def _test(session): async with session.get('https://example.com') as resp: assert resp.status == 200, resp.status ``` -------------------------------- ### Python Extension for Recording Response Times Source: https://molotov.readthedocs.io/en/stable/_sources/extending.rst.txt This Python module can be used as a Molotov extension to record and print the average response time of all requests made during a load test. Ensure the module is importable by Molotov. ```python from molotov.util import time_to_string from molotov.listeners import Listener def record_time(self, **kwargs): """Record time""" self.total_time += kwargs['duration'] self.total_requests += 1 def average_time(self, **kwargs): """Average time""" return time_to_string(self.total_time / self.total_requests) class MyListener(Listener): def __init__(self): self.total_time = 0 self.total_requests = 0 def __call__(self, **kwargs): if kwargs['event'] == 'request_end': record_time(self, **kwargs) def get_average_time(self): return average_time(self) def molotov_extension(self): return MyListener() ``` -------------------------------- ### Run Molotov with Autosizing Source: https://molotov.readthedocs.io/en/stable/tutorial Enable the autosizing feature with the `--sizing` option. Molotov will automatically adjust the number of workers per process, stopping when the failure rate exceeds the tolerance (default 5%). ```bash (venv) $ molotov --sizing loadtest.py ``` -------------------------------- ### Run Molotov Load Script Source: https://molotov.readthedocs.io/en/stable/_sources/index.rst.txt Execute a Molotov load script with specified processes, workers, and duration. The script is identified by its module name or path. ```bash $ molotov molotov/tests/example.py -p 10 -w 200 -d 60 ``` -------------------------------- ### Event Handling with molotov.events Source: https://molotov.readthedocs.io/en/stable/_sources/events.rst.txt This section describes how to register functions to receive events emitted by Molotov. Functions decorated with :func:`molotov.events` will be called when specific events occur. ```APIDOC ## Event Handling with molotov.events ### Description Register functions to receive events emitted during the load test. Decorate functions with the :func:`molotov.events` fixture. ### Method N/A (This is a programmatic API, not an HTTP endpoint) ### Endpoint N/A ### Parameters N/A ### Request Example N/A ### Response N/A ## Supported Events ### sending_request - **session** (object) - The current HTTP session. - **request** (object) - The request object being sent. ### response_received - **session** (object) - The current HTTP session. - **response** (object) - The response object received. - **request** (object) - The request object that was sent. ### current_workers - **workers** (int) - The current number of active workers. ### scenario_start - **scenario** (object) - The scenario being started. - **wid** (int) - The worker ID. ### scenario_success - **scenario** (object) - The scenario that succeeded. - **wid** (int) - The worker ID. ### scenario_failure - **scenario** (object) - The scenario that failed. - **exception** (object) - The exception that occurred. - **wid** (int) - The worker ID. ### Example Usage ```python import molotov @molotov.events("sending_request", "response_received") def my_event_handler(**kwargs): event_name = kwargs.get('event_name') if event_name == "sending_request": print(f"Sending request: {kwargs.get('request').method} {kwargs.get('request').url}") elif event_name == "response_received": print(f"Received response: {kwargs.get('response').status_code}") # Molotov will automatically discover and use this handler. ``` ``` -------------------------------- ### Define Multiple Molotov Scenarios with Weights Source: https://molotov.readthedocs.io/en/stable/tutorial Add multiple scenarios to your load test script and assign weights to control their execution frequency. Molotov calculates the probability of running each scenario based on its weight relative to the sum of all weights. ```python from molotov import scenario @scenario(weight=20) async def _test(session): async with session.get('https://example.com') as resp: assert resp.status == 200, resp.status @scenario(weight=20) async def _test2(session): # do something @scenario(weight=60) async def _test3(session): # do something different ``` -------------------------------- ### Run Molotov Test with Multiple Processes and Workers Source: https://molotov.readthedocs.io/en/stable/tutorial Achieve higher levels of concurrency by running multiple processes, each with its own set of workers, using the `-p` and `-w` flags. This distributes the load across more resources. ```bash (venv) $ molotov -w 10 -p 4 -d 30 -x loadtest.py ``` -------------------------------- ### Global Variable Helpers Source: https://molotov.readthedocs.io/en/stable/helpers Functions to set and retrieve global variables for use across test fixtures. ```APIDOC ## molotov.set_var(name, value) ### Description Sets a global variable that can be accessed in various test fixtures or tests. ### Parameters - **name** (string) - Required - The name of the variable. - **value** (object) - Required - The object to store. ## molotov.get_var(name, factory=None) ### Description Retrieves a global variable by name. If the variable is not set and a factory is provided, the factory is called to set the variable. ### Parameters - **name** (string) - Required - The name of the variable. - **factory** (callable) - Optional - A function to initialize the variable if it does not exist. ``` -------------------------------- ### Register Event Handlers with molotov.events() Source: https://molotov.readthedocs.io/en/stable/events Decorate asynchronous functions with `molotov.events()` to receive specific events. These functions directly impact performance, so use them judiciously. Supported events include `sending_request`, `response_received`, `current_workers`, `scenario_start`, `scenario_success`, and `scenario_failure`. ```python import molotov @molotov.events() async def print_request(event, **info): if event == "sending_request": print("=>") @molotov.events() async def print_response(event, **info): if event == "response_received": print("<=") ``` -------------------------------- ### Scenario Decorator Source: https://molotov.readthedocs.io/en/stable/fixtures The scenario() decorator registers a function as a Molotov test scenario. It accepts optional arguments for weight, delay, and name. ```APIDOC ## molotov.scenario ### Description Decorator to register a function as a Molotov test. ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Arguments - **weight** (integer) - Optional - Used by Molotov when scenarios are randomly picked. Higher values increase likelihood of selection. Defaults to 1. Ignored if `scenario_picker` is used. - **delay** (float) - Optional - Seconds to sleep after the scenario completes. Defaults to 0.0. This is added to the general `--delay` argument. - **name** (string) - Optional - Name of the scenario. Defaults to the function's `__name__` attribute. ### Notes The decorated function receives an `aiohttp.ClientSession` instance. ### Request Example ```python import molotov @molotov.scenario(weight=100, delay=0.5, name="my_scenario") async def my_scenario(session): # Scenario logic here pass ``` ### Response None directly from the decorator, the decorated function's return value is not used by Molotov. ``` -------------------------------- ### Global Variable Management Source: https://molotov.readthedocs.io/en/stable/_sources/helpers.rst.txt Functions to store and retrieve objects globally across test fixtures and tests. ```APIDOC ## Global Variable Functions ### Description Use `set_var` and `get_var` to share objects across various test fixtures or tests. ### Functions - **set_var(name, value)**: Sets a global variable. - **get_var(name)**: Retrieves a global variable. ``` -------------------------------- ### Run Molotov Test with Multiple Workers Source: https://molotov.readthedocs.io/en/stable/tutorial Increase the concurrency of the load test by specifying the number of workers using the `-w` flag. This allows for more simultaneous requests. ```bash (venv) $ molotov -w 10 -d 30 -x loadtest.py ``` -------------------------------- ### Hook into Events Source: https://molotov.readthedocs.io/en/stable/examples Use the @events decorator to track request/response lifecycles and scenario execution. ```python import molotov @molotov.events() async def print_request(event, **info): if event == "sending_request": print("=>") @molotov.events() async def print_response(event, **info): if event == "response_received": print("<=") @molotov.scenario(100) async def scenario_one(session): async with session.get("http://localhost:8080") as resp: res = await resp.json() assert res["result"] == "OK" assert resp.status == 200 ``` ```python import molotov import time _T = {} def _now(): return time.time() * 1000 @molotov.events() async def record_time(event, **info): req = info.get("request") if event == "sending_request": _T[req] = _now() elif event == "response_received": _T[req] = _now() - _T[req] @molotov.global_teardown() def display_average(): average = sum(_T.values()) / len(_T) print("\nAverage response time %dms" % average) ``` ```python import molotov import time concurs = [] # [(timestamp, worker count)] def _now(): return time.time() * 1000 @molotov.events() async def record_time(event, **info): if event == "current_workers": concurs.append((_now(), info["workers"])) @molotov.global_teardown() def display_average(): print("\nconcurrencies: %s", concurs) delta = max(ts for ts, _ in concurs) - min(ts for ts, _ in concurs) average = sum(value for _, value in concurs) * 1000 / delta print("\nAverage concurrency: %.2f VU/s" % average) ``` ```python import json import molotov import time _T = {} def _now(): return time.time() * 1000 @molotov.events() async def record_time(event, **info): if event == "scenario_start": scenario = info["scenario"] index = (info["wid"], scenario["name"]) _T[index] = _now() if event == "scenario_success": scenario = info["scenario"] index = (info["wid"], scenario["name"]) start_time = _T.pop(index, None) duration = int(_now() - start_time) if start_time is not None: print( json.dumps( { "ts": time.time(), "type": "scenario_success", "name": scenario["name"], "duration": duration, } ) ) elif event == "scenario_failure": scenario = info["scenario"] exception = info["exception"] index = (info["wid"], scenario["name"]) start_time = _T.pop(index, None) duration = int(_now() - start_time) ``` -------------------------------- ### Molotov Runner Usage Source: https://molotov.readthedocs.io/en/stable/_sources/cli.rst.txt The Molotov runner is used to execute load tests. You can point it to a scenario module or a specific path. ```APIDOC ## Molotov Runner Usage ### Description Use the **molotov** runner to execute load tests by specifying a scenario module or path. ### Method N/A (Command-line tool) ### Endpoint N/A (Command-line tool) ### Parameters This section is dynamically generated by `argparse` from the `molotov.run` module. ### Request Example ```bash molotov ``` ### Response N/A (Command-line tool output varies based on execution) ``` -------------------------------- ### Calculate scenario execution frequency Source: https://molotov.readthedocs.io/en/stable/_sources/tutorial.rst.txt The formula used by Molotov to determine how often a scenario is executed based on weights. ```text scenario_weight / sum(scenario weights) ``` -------------------------------- ### Log Scenario Failure Source: https://molotov.readthedocs.io/en/stable/examples Logs a scenario failure event with details such as timestamp, type, name, exception, error message, and duration. This is typically used within event handlers. ```python if start_time is not None: print( json.dumps( { "ts": time.time(), "type": "scenario_failure", "name": scenario["name"], "exception": exception.__class__.__name__, "errorMessage": str(exception), "duration": duration, } ) ) ``` -------------------------------- ### @molotov.events() Decorator Source: https://molotov.readthedocs.io/en/stable/events Registers a coroutine function to receive events emitted during the load test execution. ```APIDOC ## @molotov.events() ### Description Registers a function to be called whenever Molotov emits an event during a load test. The decorated function must be a coroutine. ### Parameters - **event** (string) - The name of the event being triggered. - ****info** (dict) - Keyword arguments specific to the event type. ### Supported Events - **sending_request**: session, request - **response_received**: session, response, request - **current_workers**: workers - **scenario_start**: scenario, wid - **scenario_success**: scenario, wid - **scenario_failure**: scenario, exception, wid ### Request Example ```python import molotov @molotov.events() async def print_request(event, **info): if event == "sending_request": print("=>") ``` ```