Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Alternative grouping #1858

Merged
merged 8 commits into from
Aug 21, 2021
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion docs/testing-other-systems.rst
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
.. _testing-other-systems:

========================
Testing non-HTTP systems
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe split this into two sections? ”Testing non-HTTP systems” and ”Testing requests-based SDKs”?

Testing other systems
========================

Locust only comes with built-in support for HTTP/HTTPS but it can be extended to load test almost any system. You do this by writing a custom client that triggers :py:attr:`request <locust.event.Events.request>`
Expand Down Expand Up @@ -44,4 +44,13 @@ gRPC client, base User and example usage:

.. literalinclude:: ../examples/grpc/locustfile.py


Example: Patching over SDK's that wrap around Session objects
=============================================================

If you have a prebuilt SDK for a target system that is a essentially a wrapper for Session object. You can use the a pattern of patching over the internal session object with the locust provided one:

.. literalinclude:: ../examples/sdk_session_patching/session_patch_locustfile.py


For more examples of user types, see `locust-plugins <https://github.com/SvenskaSpel/locust-plugins#users>`_ (it has users for WebSocket/SocketIO, Kafka, Selenium/WebDriver and more)
29 changes: 29 additions & 0 deletions docs/writing-a-locustfile.rst
Original file line number Diff line number Diff line change
Expand Up @@ -546,6 +546,35 @@ Example:
for i in range(10):
self.client.get("/blog?id=%i" % i, name="/blog?id=[id]")

There may be situations where passing in a parameter into request function is not possible, such as when interacting with libraries/SDK's that
wrap a Requests session. An alternative say of grouping requests is provided By setting the ``client.group_name`` attribute

.. code-block:: python

# Statistics for these requests will be grouped under: /blog/?id=[id]
self.client.group_name="/blog?id=[id]"
for i in range(10):
self.client.get("/blog?id=%i" % i)
self.client.group_name=None

If You want to chain multiple groupings with minimal boilerplate, you can use the ``client.group()`` context manager.

.. code-block:: python

@task
def multiple_groupings_example(self):

# Statistics for these requests will be grouped under: /blog/?id=[id]
with self.client.group(name="/blog?id=[id]"):
for i in range(10):
self.client.get("/blog?id=%i" % i)

# Statistics for these requests will be grouped under: /article/?id=[id]
with self.client.group(name="/article?id=[id]"):
for i in range(10):
self.client.get("/article?id=%i" % i)



HTTP Proxy settings
-------------------
Expand Down
24 changes: 24 additions & 0 deletions examples/sdk_session_patching/session_patch_locustfile.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import locust
from locust.user import task
from archivist.archivist import Archivist # Example SDK under test


class ArchivistUser(locust.HttpUser):

def on_start(self):
AUTH_TOKEN = None

with open("auth.text", "r") as f:
AUTH_TOKEN = f.read()

# Start an instance of of the SDK
self.arch: Archivist = Archivist(url=self.host, auth=AUTH_TOKEN)
# overwrite the internal _session attribute with the locust session
self.arch._session = self.client

@task
def Create_assets(self):
""" User creates assets as fast as possible"""

while True:
self.arch.assets.create(behaviours=[], attrs={})
19 changes: 19 additions & 0 deletions locust/clients.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from requests import Request, Response
from requests.auth import HTTPBasicAuth
from requests.exceptions import InvalidSchema, InvalidURL, MissingSchema, RequestException
from contextlib import contextmanager

from urllib.parse import urlparse, urlunparse

Expand Down Expand Up @@ -51,6 +52,10 @@ def __init__(self, base_url, request_event, user, *args, **kwargs):
self.request_event = request_event
self.user = user

# User can group name, or use the group context manager to gather performance statistics under a specific name
# This is an alternative to passing in the "name" parameter to the requests function
self.group_name = None

# Check for basic authentication
parsed_url = urlparse(self.base_url)
if parsed_url.username and parsed_url.password:
Expand All @@ -72,6 +77,16 @@ def _build_url(self, path):
else:
return "%s%s" % (self.base_url, path)

@contextmanager
def group(self, *, name: str):
"""Group requests using the "with" keyword"""

self.group_name = name
try:
yield
finally:
self.group_name = None

def request(self, method, url, name=None, catch_response=False, context={}, **kwargs):
"""
Constructs and sends a :py:class:`requests.Request`.
Expand Down Expand Up @@ -102,6 +117,10 @@ def request(self, method, url, name=None, catch_response=False, context={}, **kw
:param cert: (optional) if String, path to ssl client cert file (.pem). If Tuple, ('cert', 'key') pair.
"""

# if group name has been set and no name parameter has been passed in; set the name parameter to group_name
if self.group_name and not name:
name = self.group_name

# prepend url with hostname unless it's already an absolute URL
url = self._build_url(url)
start_time = time.perf_counter()
Expand Down