Asyncio http mocking. Similar to the responses library used for 'requests'

Related tags

Testing aresponses
Overview

aresponses

image image build status Code style: black

an asyncio testing server for mocking external services

Features

  • Fast mocks using actual network connections
  • allows mocking some types of network issues
  • use regular expression matching for domain, path, method, or body
  • works with https requests as well (by switching them to http requests)
  • works with callables

Usage

Add routes and responses via the aresponses.add method:

def add(
    host_pattern=ANY, 
    path_pattern=ANY, 
    method_pattern=ANY, 
    response="", 
    *, 
    route=None, 
    body_pattern=ANY, m
    match_querystring=False, 
    repeat=1
    )

When a request is received the first matching response will be returned and removed from the routing table. The response argument can be either a string, Response, dict, or list. Use aresponses.Response when you need to do something more complex.

Note that version >=2.0 requires explicit assertions!

@pytest.mark.asyncio
async def test_simple(aresponses):
    aresponses.add("google.com", "/api/v1/", "GET", response="OK")
    aresponses.add('foo.com', '/', 'get', aresponses.Response(text='error', status=500))

    async with aiohttp.ClientSession() as session:
        async with session.get("http://google.com/api/v1/") as response:
            text = await response.text()
            assert text == "OK"
        
        async with session.get("https://foo.com") as response:
            text = await response.text()
            assert text == "error"

    aresponses.assert_plan_strictly_followed()

Assertions

In aresponses 1.x requests that didn't match a route stopped the event loop and thus forced an exception. In aresponses >2.x it's required to make assertions at the end of the test.

There are three assertions functions provided:

  • aresponses.assert_no_unused_routes Raises UnusedRouteError if all the routes defined were not used up.
  • aresponses.assert_called_in_order - Raises UnorderedRouteCallError if the routes weren't called in the order they were defined.
  • aresponses.assert_all_requests_matched - Raises NoRouteFoundError if any requests were made that didn't match to a route. It's likely but not guaranteed that your code will throw an exception in this situation before the assertion is reached.

Instead of calling these individually, it's recommended to call aresponses.assert_plan_strictly_followed() at the end of each test as it runs all three of the above assertions.

Regex and Repeat

host_pattern, path_pattern, method_pattern and body_pattern may be either strings (exact match) or regular expressions.

The repeat argument permits a route to be used multiple times.

If you want to just blanket mock a service, without concern for how many times its called you could set repeat to a large number and not call aresponses.assert_plan_strictly_followed or arespones.assert_no_unused_routes.

@pytest.mark.asyncio
async def test_regex_repetition(aresponses):
    aresponses.add(re.compile(r".*\.?google\.com"), response="OK", repeat=2)

    async with aiohttp.ClientSession() as session:
        async with session.get("http://google.com") as response:
            text = await response.text()
            assert text == "OK"

        async with session.get("http://api.google.com") as response:
            text = await response.text()
            assert text == "OK"

    aresponses.assert_plan_strictly_followed()

Json Responses

As a convenience, if a dict or list is passed to response then it will create a json response. A aiohttp.web_response.json_response object can be used for more complex situations.

@pytest.mark.asyncio
async def test_json(aresponses):
    aresponses.add("google.com", "/api/v1/", "GET", response={"status": "OK"})

    async with aiohttp.ClientSession() as session:
        async with session.get("http://google.com/api/v1/") as response:
            assert {"status": "OK"} == await response.json()

    aresponses.assert_plan_strictly_followed()

Custom Handler

Custom functions can be used for whatever other complex logic is desired. In example below the handler is set to repeat infinitely and always return 500.

import math

@pytest.mark.asyncio
async def test_handler(aresponses):
    def break_everything(request):
        return aresponses.Response(status=500, text=str(request.url))

    aresponses.add(response=break_everything, repeat=math.inf)

    async with aiohttp.ClientSession() as session:
        async with session.get("http://google.com/api/v1/") as response:
            assert response.status == 500

Passthrough

Pass aresponses.passthrough into the response argument to allow a request to bypass mocking.

    aresponses.add('httpstat.us', '/200', 'get', aresponses.passthrough)

Inspecting history

History of calls can be inspected via aresponses.history which returns the namedTuple RoutingLog(request, route, response)

@pytest.mark.asyncio
async def test_history(aresponses):
    aresponses.add(response=aresponses.Response(text="hi"), repeat=2)

    async with aiohttp.ClientSession() as session:
        async with session.get("http://foo.com/b") as response:
            await response.text()
        async with session.get("http://bar.com/a") as response:
            await response.text()

    assert len(aresponses.history) == 2
    assert aresponses.history[0].request.host == "foo.com"
    assert aresponses.history[1].request.host == "bar.com"
    assert "Route(" in repr(aresponses.history[0].route)
    aresponses.assert_plan_strictly_followed()

Context manager usage

import aiohttp
import pytest
import aresponses


@pytest.mark.asyncio
async def test_foo(event_loop):
    async with aresponses.ResponsesMockServer(loop=event_loop) as arsps:
        arsps.add('foo.com', '/', 'get', 'hi there!!')
        arsps.add(arsps.ANY, '/', 'get', arsps.Response(text='hey!'))
        
        async with aiohttp.ClientSession(loop=event_loop) as session:
            async with session.get('http://foo.com') as response:
                text = await response.text()
                assert text == 'hi'
            
            async with session.get('https://google.com') as response:
                text = await response.text()
                assert text == 'hey!'
        

working with pytest-aiohttp

If you need to use aresponses together with pytest-aiohttp, you should re-initialize main aresponses fixture with loop fixture

from aresponses import ResponsesMockServer

@pytest.fixture
async def aresponses(loop):
    async with ResponsesMockServer(loop=loop) as server:
        yield server

If you're trying to use the aiohttp_client test fixture then you'll need to mock out the aiohttp loop fixture instead:

@pytest.fixture
def loop(event_loop):
    """replace aiohttp loop fixture with pytest-asyncio fixture"""
    return event_loop

Contributing

Dev environment setup

  • install pyenv and pyenv-virtualenv - Makes it easy to install specific versions of python and switch between them. Make sure you install the virtualenv bash hook
  • git clone the repo and cd into it.
  • make init - installs proper version of python, creates the virtual environment, activates it and installs all the requirements

Submitting a feature request

  • git checkout -b my-feature-branch
  • make some cool changes
  • make autoformat
  • make test
  • make lint
  • create pull request

Updating package on pypi

  • make deploy

Changelog

2.1.4

  • fix: don't assume utf8 request contents

2.1.3

  • accidental no-op release

2.1.2

  • documentation: add pypi documentation

2.1.1

  • bugfix: RecursionError when aresponses is used in more than 1000 tests (#63)

2.1.0

  • feature: add convenience method add_local_passthrough
  • bugfix: fix https subrequest mocks. support aiohttp_client compatibility

2.0.2

  • bugfix: ensure request body is available in history

2.0.0

Warning! Breaking Changes!

  • breaking change: require explicit assertions for test failures
  • feature: autocomplete works in intellij/pycharm
  • feature: can match on body of request
  • feature: store calls made
  • feature: repeated responses
  • bugfix: no longer stops event loop
  • feature: if dict or list is passed into response, a json response will be generated

1.1.2

  • make passthrough feature work with binary data

1.1.1

  • regex fix for Python 3.7.0

1.1.0

  • Added passthrough option to permit live network calls
  • Added example of using a callable as a response

1.0.0

  • Added an optional match_querystring argument that lets you match on querystring as well

Contributors

Comments
  • Combining aresponses with aiohttp_client

    Combining aresponses with aiohttp_client

    Hello,

    Assume a code under test which is a simple aiohttp web server that does an external HTTP call using aiohttp's ClientSession: https://repl.it/@zopieux/aiohttpAresponses#app.py

    To test my server works, I can write a small test: https://repl.it/@zopieux/aiohttpAresponses#test.py

    and this works. Sadly, this is non-hermetic, it makes real requests on the internet to httpbin.org.

    I know, I'll use aresponses! So I write https://repl.it/@zopieux/aiohttpAresponses#testaresponses.py

    If you run this REPL, you'll see the first "internet enabled" test passes, but the aresponses test hangs indefinitely on the client.get() line in the test.

    I tried debugging it but couldn't find much. I can see the two aiohttp test webservers spawned on different ports on 127.0.0.1.

    My understanding is that aresponses intercepts all the ClientSession requests, even the ones that are supposed to go to the other test server created by aiohttp_client. I thought I could add:

    aresponses.add("127.0.0.1", response=aresponses.passthrough)
    aresponses.add("127.0.0.1:4241", response=aresponses.passthrough)  # 4241 is aiohttp test port
    

    to make it explicit, but aresponses doesn't care, the calls are not forwarded to the aiohttp_client server and this test hangs for minutes (TCP timeout I suppose).

    How can I make aresponses play nice with aiohttp_client?

    bug 
    opened by zopieux 11
  • fix: https passthrough. aiohttp_client compatability

    fix: https passthrough. aiohttp_client compatability

    This is an alternative to #55 that maintains compatibility with pytest-asyncio and fixes #51 and fixes #52.

    The is_ssl method was monkey-patched in a synchronous way that couldn't work when simultaneous requests needed it both patched and not-patched. Instead of undoing the ClientRequest monkey-patching we create DirectClientRequest and have our passthrough session use it instead.

    For compatibility between aresponses and aiohttp_client fixtures we use a work-around recommended by @serjshevchenko (https://github.com/pytest-dev/pytest-asyncio/issues/170#issuecomment-706114516) that replaces the aiohttp_client loop fixture with the event loop provided by pytest-asycnio. The hanging was caused by different fixtures running on different event loops.

    Workaround example:

    @pytest.fixture
    def loop(event_loop):
        """replace aiohttp loop fixture with pytest-asyncio fixture"""
        return event_loop
    

    Also added add_local_passthrough which is useful when working with your own application server or other local resources. To work with assert_plan_strictly_followed you should pass in the number of times it will be called as the repeat argument.

    It's equivalent to

    LOCALHOST = re.compile(r"127\.0\.0\.1:?\d{0,5}")
    aresponses.add(
        host_pattern=LOCALHOST,
        repeat=aresponses.INFINITY,
        response=aresponses.passthrough
        )
    

    @elupus @Zopieux I think I prefer this solution over #55 although it does require the developer to manually override the loop fixture. I'll leave these pull requests open for a few days to give you both an opportunity to comment.

    opened by brycedrennan 9
  • Make responses reusable

    Make responses reusable

    This makes it possible to define session-wide mocks to intercept requests. The response object is single-use so we need to make a copy of it after finding a match.

    opened by layday 8
  • Support `pytest_aiohttp`

    Support `pytest_aiohttp`

    At the present pytest_aiohttp doesn't work correct with pytest_asyncio (pytest_aiohttp issue and pytest_asyncio issue ). On the other hand aresponses doesn't require pytest_asyncio directly and can be executed without it. But aresponses depends on event_loop fixture here. So the main questions - can we use existing fixture event_loop from asyncio or loop from aiohttp on the fly for the aresponses fixture initialization?

    """Test script to show aresponses work without pytest_asyncio
    Steps:
    - install all required packages as `pip install pytest aiohttp pytest_aiohttp aresponses`
    - run script as `pytest test_example.py -p no:asyncio
    """
    import pytest
    import aiohttp
    
    
    # Simple server definition
    async def handle(request):
        return aiohttp.web.Response(text="test response")
    
    
    def create_app():
        app = aiohttp.web.Application()
        app.add_routes([aiohttp.web.get('/', handle)])
        return app
    
    
    # Tests
    @pytest.fixture
    async def client(aiohttp_client):
        app = create_app()
        return await aiohttp_client(app)
    
    
    async def test_root_url(client):
        resp = await client.get('/')
        assert resp.status == 200
        assert await resp.text() == "test response"
    
    
    # this additional fixture required by aresponses
    # by default aresponses depends on `event_loop` fixture defined at the pytest_asyncio
    @pytest.fixture
    def event_loop(loop):
        return loop
    
    
    async def test_aresponses_without_aiohttp(aresponses):
        aresponses.add('foo.com', '/', 'get', 'hi there!!')
        url = 'http://foo.com'
        async with aiohttp.ClientSession() as session:
            async with session.get(url) as response:
                text = await response.text()
                assert text == 'hi there!!'
    
    opened by ikhlestov 6
  • passthrough: make it work with binary data, closes #35

    passthrough: make it work with binary data, closes #35

    Sorry, I can't think of a public API service to test with, like httpstat.us, but in theory this should fix my problem and doesn't break the existing test.

    opened by gjcarneiro 5
  • passthrough support

    passthrough support

    If the response type is PassThrough, the test server acts as a proxy. This is not bullet-proof, with use of e.g. asyncio.gather, things can mess up (but it was a problem even with the code before this commit).

    From the remote server's headers, only the "Content-Type" is preserved (which is enough in many cases). Other headers are filtered out because may cause problems (for example content-encoding).

    The PassThrough supports HTTPS. The support is maybe not very clean but it works. The information about HTTPS usage in the client's request is added to the request as a header, then the HTTPS is suppressed (the request is converted to HTTP, as it was before this commit). The proxy server reads the HTTPS information from the headers and modifies the request's scheme according to it.

    opened by p4l1ly 5
  • Restore the constructor to prevent RecursionError at 1000+ tests

    Restore the constructor to prevent RecursionError at 1000+ tests

    When the fixture aresponses is used in more than 1000 consecutive tests (empirically, 940+), it hits the RecursionError when calling its ClientRequest's constructor. All tests that follow also fail regardless of what and how they test.

    Caused by the constructor function being replaced, calling the original pre-replacement function, and never restoring the original function. As a result, every test adds +1 to the recursion, until it reaches some limits where Python raises a RecursionError for all frames (~995 frames from the entry point for a pure-Python script, and roughly ~940 level within pytest's context).

    For the testing purposes, a limit is artificially lowered to 100 (of which 40-50 levels are taken by pytest & co, 10 for aresponses, and nearly 40 are consumed by recursion). The default 1000 is too slow (several seconds, not instant enough). Besides, it can vary in the future.


    A sample stack trace without the fix (for search engine indexing):

    Traceback (most recent call last):
      File "/Users/nolar/src/aresponses/tests/test_server.py", line 397, in test_not_exceeding_recursion_limit
        async with session.get("http://fake-host"):
      File "/Users/nolar/.pyenv/versions/3.9.0/envs/arsp39/lib/python3.9/site-packages/aiohttp/client.py", line 1117, in __aenter__
        self._resp = await self._coro
      File "/Users/nolar/.pyenv/versions/3.9.0/envs/arsp39/lib/python3.9/site-packages/aiohttp/client.py", line 492, in _request
        req = self._request_class(
      File "/Users/nolar/src/aresponses/aresponses/main.py", line 282, in new_init
        self._old_init(_self, *largs, **kwargs)
      File "/Users/nolar/src/aresponses/aresponses/main.py", line 282, in new_init
        self._old_init(_self, *largs, **kwargs)
      File "/Users/nolar/src/aresponses/aresponses/main.py", line 282, in new_init
        self._old_init(_self, *largs, **kwargs)
    
      [Previous line repeated 38 more times]
    
      File "/Users/nolar/.pyenv/versions/3.9.0/envs/arsp39/lib/python3.9/site-packages/aiohttp/client_reqrep.py", line 305, in __init__
        self.update_host(url)
      File "/Users/nolar/.pyenv/versions/3.9.0/envs/arsp39/lib/python3.9/site-packages/aiohttp/client_reqrep.py", line 369, in update_host
        username, password = url.user, url.password
      File "/Users/nolar/.pyenv/versions/3.9.0/envs/arsp39/lib/python3.9/site-packages/yarl/_url.py", line 50, in __get__
        val = self.wrapped(inst)
      File "/Users/nolar/.pyenv/versions/3.9.0/envs/arsp39/lib/python3.9/site-packages/yarl/_url.py", line 476, in password
        return self._UNQUOTER(self.raw_password)
      File "/Users/nolar/.pyenv/versions/3.9.0/envs/arsp39/lib/python3.9/site-packages/yarl/_url.py", line 467, in raw_password
        return self._val.password
      File "/Users/nolar/.pyenv/versions/3.9.0/lib/python3.9/urllib/parse.py", line 154, in password
        return self._userinfo[1]
      File "/Users/nolar/.pyenv/versions/3.9.0/lib/python3.9/urllib/parse.py", line 189, in _userinfo
        userinfo, have_info, hostinfo = netloc.rpartition('@')
    RecursionError: maximum recursion depth exceeded while calling a Python object
    
    
    opened by nolar 4
  • Usage with pytest-aiohttp

    Usage with pytest-aiohttp

    The documentation states that with pytest-aiohttp one should re-initialise the mock server like below

    from aresponses import ResponsesMockServer
    
    @pytest.fixture
    async def aresponses(loop):
        async with ResponsesMockServer(loop=loop) as server:
            yield server
    

    However following this documentation and passing the aresponses-fixture to my test function, I get the following error

    tests/test_foo.py::test_foo[pyloop]
      /Users/mvalkonen/Library/Caches/pypoetry/virtualenvs/test-py3.7/lib/python3.7/site-packages/aresponses/main.py:53: DeprecationWarning: The object should be created from async function
        srv = Server(self._handler, loop=self._loop, debug=True, **kwargs)
    

    I can reproduce this with the following example

    @pytest.fixture
    async def aresponses(loop):
        async with ResponsesMockServer(loop=loop) as server:
            yield server
    
    
    async def test_foo(aresponses):
        pass
    

    How should one use aresponses with pytest-aiohttp?

    opened by mvalkon 4
  • Removing urls once reached

    Removing urls once reached

    Hi! Thanks for writing this!

    I was working on integrating this into one of my first aiohttp projects and I could not figure out why the url I'd added was going away after the very first called had reached it. After a little while, I went to read the source and found https://github.com/CircleUp/aresponses/blob/master/aresponses/main.py#L93.

    I can definitely see an argument for having this but I think it would be helpful to craft an error message saying why it's been removed. Or, the call to delete the route could simply be removed.

    This functionality is different than every other mock utility I've used. IME the urls are always available and it is left to the caller to establish counts or use some sort of history storage mechanism to verify which paths were hit and how frequently. Overall, I think this behavior is surprising to users and can be somewhat difficult to diagnose. Also, the workaround (at least in my case) has been to add the same url in a loop so that my test can make repeated requests to the same resource.

    What do you think?

    Thanks again :)

    enhancement 
    opened by derwolfe 4
  • Dependency on pytest-asyncio breaks support for aiohttp

    Dependency on pytest-asyncio breaks support for aiohttp

    When trying to run a test with the workaround provided for compatibility with aiohttp, the test fail with an error:

    Test session starts (platform: darwin, Python 3.7.3, pytest 5.4.1, pytest-sugar 0.9.3)
    rootdir: /Users/joakim/src/hass/nibeuplink
    plugins: timeout-1.3.4, aresponses-2.0.0, mock-3.1.0, requests-mock-1.7.0, sugar-0.9.3, cov-2.5.1, threadleak-0.2.0, aiohttp-0.3.0, asyncio-0.14.0
    collecting ... 
    
    ――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― test_get_system ――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
    
    aresponses = <aresponses.main.ResponsesMockServer object at 0x10745a978>, uplink = <nibeuplink.uplink.Uplink object at 0x10745ac18>
    
        async def test_get_system(aresponses, uplink: Uplink):
    >       result = await uplink.get_system(123)
    
    tests/test_uplink.py:30: 
    _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
    src/nibeuplink/uplink.py:175: in get_system
        return await self.get(f"systems/{system_id}")
    src/nibeuplink/uplink.py:74: in get
        "GET", f"{self.base}/api/v1/{url}", *args, **kwargs
    src/nibeuplink/session.py:175: in request
        response = await self.session.request(*args, auth=await self._get_auth(), **kw)
    ../home-assistant/venv/lib/python3.7/site-packages/aiohttp/client.py:426: in _request
        with timer:
    _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
    
    self = <aiohttp.helpers.TimerContext object at 0x10745a0f0>
    
        def __enter__(self) -> BaseTimerContext:
            task = current_task(loop=self._loop)
        
            if task is None:
    >           raise RuntimeError('Timeout context manager should be used '
                                   'inside a task')
    E           RuntimeError: Timeout context manager should be used inside a task
    
    ../home-assistant/venv/lib/python3.7/site-packages/aiohttp/helpers.py:579: RuntimeError
    
     tests/test_uplink.py ⨯                                                                                                                                                          100% ██████████
    =================================================================================== short test summary info ====================================================================================
    FAILED tests/test_uplink.py::test_get_system - RuntimeError: Timeout context manager should be used inside a task
    

    After doing a pip uninstall pytest-asyncio, the test succeeds. Just having pytest-asyncio installed breaks behavior of aiohttp tests.

    Would it be possible to have aresponses check which pytest aio handler is installed and use the loop accordingly, then drop the requirement from setup.py (or make it either or).

    opened by elupus 3
  • Don't close event loop passed as argument

    Don't close event loop passed as argument

    Hi. I have a proposal to don't close the loop when there is no match. Code snippet:

        async def _find_response(self, request):
            host, path, path_qs, method = request.host, request.path, request.path_qs, request.method
            logger.info(f"Looking for match for {host} {path} {method}")
            i = 0
            host_matched = False
            path_matched = False
            for host_pattern, path_pattern, method_pattern, response, match_querystring in self._responses:
                if _text_matches_pattern(host_pattern, host):
                    host_matched = True
                    if (not match_querystring and _text_matches_pattern(path_pattern, path)) or (
                        match_querystring and _text_matches_pattern(path_pattern, path_qs)
                    ):
                        path_matched = True
                        if _text_matches_pattern(method_pattern, method.lower()):
                            del self._responses[i]
    
                            if callable(response):
                                if asyncio.iscoroutinefunction(response):
                                    return await response(request)
                                return response(request)
    
                            if isinstance(response, str):
                                return self.Response(body=response)
    
                            return response
                i += 1
            self._exception = Exception(f"No Match found for {host} {path} {method}.  Host Match: {host_matched}  Path Match: {path_matched}")
            self._loop.stop()
            raise self._exception  # noqa
    
    

    This can cause errors like RuntimeError: Event loop stopped before Future completed. if this method was called in coroutine.

    opened by marcinmilewski1 3
  • aresponses does not work with aiohttp v3.8.1

    aresponses does not work with aiohttp v3.8.1

    After upgrading from aiohttp v3.7.4.post0 to version 3.8.1, my aresponses test cases fail with the below:

    Traceback (most recent call last):
      File "../lib/python3.6/site-packages/aiohttp/web_protocol.py", line 602, in finish_response
        prepare_meth = resp.prepare
    AttributeError: 'NoneType' object has no attribute 'prepare'
    
    During handling of the above exception, another exception occurred:
    
    Traceback (most recent call last):
      File "../lib/python3.6/site-packages/aiohttp/web_protocol.py", line 514, in start
        resp, reset = await task
      File "../lib/python3.6/site-packages/aiohttp/web_protocol.py", line 460, in _handle_request
        reset = await self.finish_response(request, resp, start_time)
      File "../lib/python3.6/site-packages/aiohttp/web_protocol.py", line 605, in finish_response
        raise RuntimeError("Missing return " "statement on request handler")
    RuntimeError: Missing return statement on request handler
    

    If I downgrade to 3.7.4.post0 I don't experience this issue.

    opened by robertBrnnn 0
Owner
null
A utility for mocking out the Python Requests library.

Responses A utility library for mocking out the requests Python library. Note Responses requires Python 2.7 or newer, and requests >= 2.0 Installing p

Sentry 3.8k Jan 3, 2023
A utility for mocking out the Python Requests library.

Responses A utility library for mocking out the requests Python library. Note Responses requires Python 2.7 or newer, and requests >= 2.0 Installing p

Sentry 3.1k Feb 8, 2021
A mocking library for requests

httmock A mocking library for requests for Python 2.7 and 3.4+. Installation pip install httmock Or, if you are a Gentoo user: emerge dev-python/httm

Patryk Zawadzki 419 Feb 2, 2021
HTTP client mocking tool for Python - inspired by Fakeweb for Ruby

HTTPretty 1.0.5 HTTP Client mocking tool for Python created by Gabriel Falcão . It provides a full fake TCP socket module. Inspired by FakeWeb Github

Gabriel Falcão 2k Jan 6, 2023
HTTP client mocking tool for Python - inspired by Fakeweb for Ruby

HTTPretty 1.0.5 HTTP Client mocking tool for Python created by Gabriel Falcão . It provides a full fake TCP socket module. Inspired by FakeWeb Github

Gabriel Falcão 1.9k Feb 6, 2021
Wraps any WSGI application and makes it easy to send test requests to that application, without starting up an HTTP server.

WebTest This wraps any WSGI application and makes it easy to send test requests to that application, without starting up an HTTP server. This provides

Pylons Project 325 Dec 30, 2022
Turn any OpenAPI2/3 and Postman Collection file into an API server with mocking, transformations and validations.

Prism is a set of packages for API mocking and contract testing with OpenAPI v2 (formerly known as Swagger) and OpenAPI v3.x. Mock Servers: Life-like

Stoplight 3.3k Jan 5, 2023
API mocking with Python.

apyr apyr (all lowercase) is a simple & easy to use mock API server. It's great for front-end development when your API is not ready, or when you are

Umut Seven 55 Nov 25, 2022
Pytest support for asyncio.

pytest-asyncio: pytest support for asyncio pytest-asyncio is an Apache2 licensed library, written in Python, for testing asyncio code with pytest. asy

pytest-dev 1.1k Jan 2, 2023
Aioresponses is a helper for mock/fake web requests in python aiohttp package.

aioresponses Aioresponses is a helper to mock/fake web requests in python aiohttp package. For requests module there are a lot of packages that help u

null 402 Jan 6, 2023
Integration layer between Requests and Selenium for automation of web actions.

Requestium is a Python library that merges the power of Requests, Selenium, and Parsel into a single integrated tool for automatizing web actions. The

Tryolabs 1.7k Dec 27, 2022
ApiPy was created for api testing with Python pytest framework which has also requests, assertpy and pytest-html-reporter libraries.

ApiPy was created for api testing with Python pytest framework which has also requests, assertpy and pytest-html-reporter libraries. With this f

Mustafa 1 Jul 11, 2022
API Test Automation with Requests and Pytest

api-testing-requests-pytest Install Make sure you have Python 3 installed on your machine. Then: 1.Install pipenv sudo apt-get install pipenv 2.Go to

Sulaiman Haque 2 Nov 21, 2021
Automatically mock your HTTP interactions to simplify and speed up testing

VCR.py ?? This is a Python version of Ruby's VCR library. Source code https://github.com/kevin1024/vcrpy Documentation https://vcrpy.readthedocs.io/ R

Kevin McCarthy 2.3k Jan 1, 2023
An interactive TLS-capable intercepting HTTP proxy for penetration testers and software developers.

mitmproxy mitmproxy is an interactive, SSL/TLS-capable intercepting proxy with a console interface for HTTP/1, HTTP/2, and WebSockets. mitmdump is the

mitmproxy 29.7k Jan 2, 2023
Automatically mock your HTTP interactions to simplify and speed up testing

VCR.py ?? This is a Python version of Ruby's VCR library. Source code https://github.com/kevin1024/vcrpy Documentation https://vcrpy.readthedocs.io/ R

Kevin McCarthy 1.8k Feb 7, 2021
Language-agnostic HTTP API Testing Tool

Dredd — HTTP API Testing Framework Dredd is a language-agnostic command-line tool for validating API description document against backend implementati

Apiary 4k Jan 5, 2023
Lightweight, scriptable browser as a service with an HTTP API

Splash - A javascript rendering service Splash is a javascript rendering service with an HTTP API. It's a lightweight browser with an HTTP API, implem

Scrapinghub 3.8k Jan 3, 2023
One-stop solution for HTTP(S) testing.

HttpRunner HttpRunner is a simple & elegant, yet powerful HTTP(S) testing framework. Enjoy! ✨ ?? ✨ Design Philosophy Convention over configuration ROI

HttpRunner 3.5k Jan 4, 2023