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

Related tags

Testingaresponses
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

Test scripts etc. for experimental rollup testing

rollup node experiments Test scripts etc. for experimental rollup testing. untested, work in progress python -m venv venv source venv/bin/activate #

Diederik Loerakker 14 Jan 25, 2022
HTTP load generator, ApacheBench (ab) replacement, formerly known as rakyll/boom

hey is a tiny program that sends some load to a web application. hey was originally called boom and was influenced from Tarek Ziade's tool at tarekzia

Jaana Dogan 14.9k Jan 07, 2023
A single module to link Python ecosystem to the Web

A single module to link Python ecosystem to the Web. Have a quick look at the Gallery first to get convinced ! FAQ For any questions, please use Stack

66 Dec 21, 2022
Selenium-python but lighter: Helium is the best Python library for web automation.

Selenium-python but lighter: Helium Selenium-python is great for web automation. Helium makes it easier to use. For example: Under the hood, Helium fo

Michael Herrmann 3.2k Dec 31, 2022
Pyramid debug toolbar

pyramid_debugtoolbar pyramid_debugtoolbar provides a debug toolbar useful while you're developing your Pyramid application. Note that pyramid_debugtoo

Pylons Project 95 Sep 17, 2022
A small automated test structure using python to test *.cpp codes

Get Started Insert C++ Codes Add Test Code Run Test Samples Check Coverages Insert C++ Codes you can easily add c++ files in /inputs directory there i

Alireza Zahiri 2 Aug 03, 2022
Ab testing - The using AB test to test of difference of conversion rate

Facebook recently introduced a new type of offer that is an alternative to the current type of bidding called maximum bidding he introduced average bidding.

5 Nov 21, 2022
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 06, 2023
This package is a python library with tools for the Molecular Simulation - Software Gromos.

This package is a python library with tools for the Molecular Simulation - Software Gromos. It allows you to easily set up, manage and analyze simulations in python.

14 Sep 28, 2022
Show, Edit and Tell: A Framework for Editing Image Captions, CVPR 2020

Show, Edit and Tell: A Framework for Editing Image Captions | arXiv This contains the source code for Show, Edit and Tell: A Framework for Editing Ima

Fawaz Sammani 76 Nov 25, 2022
A toolbar overlay for debugging Flask applications

Flask Debug-toolbar This is a port of the excellent django-debug-toolbar for Flask applications. Installation Installing is simple with pip: $ pip ins

863 Dec 29, 2022
A library for generating fake data and populating database tables.

Knockoff Factory A library for generating mock data and creating database fixtures that can be used for unit testing. Table of content Installation Ch

Nike Inc. 30 Sep 23, 2022
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 02, 2023
The evaluator covering all of the metrics required by tasks within the DUE Benchmark.

DUE Evaluator The repository contains the evaluator covering all of the metrics required by tasks within the DUE Benchmark, i.e., set-based F1 (for KI

DUE Benchmark 4 Jan 21, 2022
A test fixtures replacement for Python

factory_boy factory_boy is a fixtures replacement based on thoughtbot's factory_bot. As a fixtures replacement tool, it aims to replace static, hard t

FactoryBoy project 3k Jan 05, 2023
🎓 Stepik Academy Автоматизация тестирования на Python

🎓 Stepik Academy Автоматизация тестирования на Python Запуск тестов выполняется в командной строке: pytest -v --tb=line --language=en --alluredir=all

Sergey 1 Dec 03, 2021
Pytest-rich - Pytest + rich integration (proof of concept)

pytest-rich Leverage rich for richer test session output. This plugin is not pub

Bruno Oliveira 170 Dec 02, 2022
Local continuous test runner with pytest and watchdog.

pytest-watch -- Continuous pytest runner pytest-watch a zero-config CLI tool that runs pytest, and re-runs it when a file in your project changes. It

Joe Esposito 675 Dec 23, 2022
Flexible test automation for Python

Nox - Flexible test automation for Python nox is a command-line tool that automates testing in multiple Python environments, similar to tox. Unlike to

Stargirl Flowers 941 Jan 03, 2023
pytest plugin that let you automate actions and assertions with test metrics reporting executing plain YAML files

pytest-play pytest-play is a codeless, generic, pluggable and extensible automation tool, not necessarily test automation only, based on the fantastic

pytest-dev 67 Dec 01, 2022