Docker-based integration tests

Related tags

Testingpytest-docker
Overview

Docker-based integration tests

PyPI version Build Status Python versions Code style

Description

Simple pytest fixtures that help you write integration tests with Docker and docker-compose. Specify all necessary containers in a docker-compose.yml file and and pytest-docker will spin them up for the duration of your tests.

This package is tested with Python versions 3.6, 3.7, 3.8 and 3.9, and pytest version 4, 5 and 6. Python 2 is not supported.

pytest-docker was originally created by André Caron.

Installation

Install pytest-docker with pip or add it to your test requirements. It is recommended to install docker-compose python package directly in your environment to ensure that it is available during tests. This will prevent potential dependency conflicts that can occur when the system wide docker-compose is used in tests.

Usage

Here is an example of a test that depends on a HTTP service.

With a docker-compose.yml file like this (using the httpbin service):

version: '2'
services:
  httpbin:
    image: "kennethreitz/httpbin"
    ports:
      - "8000:80"

You can write a test like this:

import pytest
import requests

from requests.exceptions import ConnectionError


def is_responsive(url):
    try:
        response = requests.get(url)
        if response.status_code == 200:
            return True
    except ConnectionError:
        return False


@pytest.fixture(scope="session")
def http_service(docker_ip, docker_services):
    """Ensure that HTTP service is up and responsive."""

    # `port_for` takes a container port and returns the corresponding host port
    port = docker_services.port_for("httpbin", 80)
    url = "http://{}:{}".format(docker_ip, port)
    docker_services.wait_until_responsive(
        timeout=30.0, pause=0.1, check=lambda: is_responsive(url)
    )
    return url


def test_status_code(http_service):
    status = 418
    response = requests.get(http_service + "/status/{}".format(status))

    assert response.status_code == status

By default this plugin will try to open docker-compose.yml in your tests directory. If you need to use a custom location, override the docker_compose_file fixture inside your conftest.py file:

import os
import pytest


@pytest.fixture(scope="session")
def docker_compose_file(pytestconfig):
    return os.path.join(str(pytestconfig.rootdir), "mycustomdir", "docker-compose.yml")

Available fixtures

All fixtures have session scope.

docker_ip

Determine the IP address for TCP connections to Docker containers.

docker_compose_file

Get an absolute path to the docker-compose.yml file. Override this fixture in your tests if you need a custom location.

docker_compose_project_name

Generate a project name using the current process PID. Override this fixture in your tests if you need a particular project name.

docker_services

Start all services from the docker compose file (docker-compose up). After test are finished, shutdown all services (docker-compose down).

docker_cleanup

Get the docker compose command to execute for test clean-up actions. Override this fixture in your tests if you need custom clean-up actions.

Development

Use of a virtual environment is recommended. See the venv package for more information.

First, install pytest-docker and its test dependencies:

pip install -e ".[tests]"

Run tests with

pytest -c setup.cfg

to make sure that the correct configuration is used. This is also how tests are run in CI.

Use black with default settings for formatting. You can also use pylint with setup.cfg as the configuration file.

Contributing

This pytest plug-in and its source code are made available to you under a MIT license. It is safe to use in commercial and closed-source applications. Read the license for details!

Found a bug? Think a new feature would make this plug-in more practical? We welcome issues and pull requests!

When creating a pull request, be sure to follow this projects conventions (see above).

Comments
  • Bring back the fallback when run without Docker

    Bring back the fallback when run without Docker

    Could you please bring back support for running without Docker & docker-compose removed in https://github.com/AndreLouisCaron/pytest-docker/commit/37901fba65759b1d4c7d78525b9590a6b96ce5a6? I was actually using that when running integration tests and it has significant value to me.

    opened by petr-k 13
  • import `docker-compose` and `docker-py` rather than fork subprocesses

    import `docker-compose` and `docker-py` rather than fork subprocesses

    Hi, this plugin looks really cool! After briefly looking over the code and some of the issues on this project I thought I'd pop in and point out that many of the problems (not having access to service logs for example) would be simplified if it were possible to have direct access to the objects exposed in the docker-compose or docker-py library APIs. Specifically, you should be able to do almost everything you can do from the command line using this class which should provide the following benefits for this codebase:

    • allow you and pytest-docker plugin users to programmatically access service and container info
    • allow you to dispense with subprocess management in your code and focus on implementing new ways to interact with docker in pytest-idiomatic ways

    Even something as simple as a more generic version of this would be fairly helpful. Here I use the docker-py library to start up container, yield it as the test fixture object, then tear it down post-yield. In an approach like this providing access to service/container logs as requested in #13 could be a configurable behavior that occurs post-yield.

    I hope you find my suggestions helpful. I would like to try my hand at implementing this if you don't have time or don't want to yourself.

    opened by waynr 11
  • feat: allow container re-use

    feat: allow container re-use

    If the spawn and cleanup commands are skipped by overriding the fixture commands to "", None, or False then existing containers can be left around.

    Having this as a native feature would be much cleaner than how I'm currently managing this. Thank you for all the work on this project, I've used it in many others with great success :)

    opened by raddessi 10
  • tests: Fix for flake8 3.6.0

    tests: Fix for flake8 3.6.0

    With the new version of flake8 a piece of the code was raising an error. With the fix the string produces is the same, but it's less ambivalent, and flake8 likes it.

    opened by butla 8
  • add --remove-orphans to docker-compose command?

    add --remove-orphans to docker-compose command?

    I got the following error while using the plugin. Does it make sense to add --remove-orphans to the docker-compose command?

    Exception: Command 'docker-compose -f "<hidden>" -p "pytest7" down -v' returned 1: """Stopping <hidden> ... 
    E           Stopping <hidden>      ... 
    E           
    Stopping <hidden> ... done
    Stopping <hidden>      ... done
    Found orphan containers (<hidden>, <hidden>, <hidden>) for this project. If you removed or renamed this service in your compose file, you can run this command with the --remove-orphans flag to clean it up.
    
    opened by vicyap 8
  • limit services started by docker_services

    limit services started by docker_services

    docker_services fixture starts all containers mentioned in the docker-compose.yaml.
    Is there some way to limit the services to some services? like docker_services(excluding_services: List)

    When in a development setting, this would be useful as we normally want to test the service with hot-reload functionality.

    I checked fixture-parameterize, but that is intended for running against all the parameters provided 1 by 1.

    opened by adityaguru149 7
  • Getting rid of tests that only test the implementation, and the path of future updates?

    Getting rid of tests that only test the implementation, and the path of future updates?

    Hey @AndreLouisCaron! I think I finally have the time to show this project some love.

    While I have some ideas for updates (e.g. #13), first I'd like to plow through the tests for this plugin itself. I think that they are tied to the implementation too much in most places, which shows in the ubiquitous mocking of subprocess.check_output. Because of that, they will all probably break with any serious refactoring.

    I think we can have a 100% coverage with like two tests that actually run docker_services and a couple tests for discrete functions. That should demonstrate that the plugin is actually working.

    I plan to submit a couple of self-contained but chained (one depending upon the other) pull requests. And, of course, I'll be mindful of not breaking anything for the current users. What do you think about this whole thing?

    I'm already tackling the problems (https://github.com/pytest-dev/pytest/issues/4193), but I wanted to hear your thoughts on that before I go far ahead.

    opened by butla 7
  • Unique container names are bad

    Unique container names are bad

    I know I can override container names, but I think the default implementation is bad.

    Currently containers are named "pytest{}".format(os.getpid()). This leads to the following problems:

    • If test runs fail or are interrupted (ctrl-c), containers may stay around. Due to the unique names they start to accumulate. Same for images.
    • Docker-compose doesn't recognize that things belong to each other, and has conflicts. For example, if you use a static network subnet, then pytest-docker fails as different runs try to use the same network, but it's named separately.

    This cost me days to debug (no docker wiz here).

    Is there a specific reason why to use the pid-based naming? I would follow simply the behavior of docker-compose:

    @pytest.fixture(scope='session')
    def docker_compose_project_name(pytestconfig):
        """ Generate a project name using the projects root directory.
    
        Override this fixture in your tests if you need a particular project name.
        """
        return "{}pytest".format(os.path.basename(str(pytestconfig.rootdir)))
    
    opened by arnuschky 7
  • Fallback still starts the container with compose?

    Fallback still starts the container with compose?

    My impression from the wording around docker_allow_fallback would be that if it returns True it doesn't try to run anything with Compose at all and just returns localhost:port, where port comes from the port mapping in the docker-compose.yaml file for that service.

    However, looking at https://github.com/AndreLouisCaron/pytest-docker/blob/master/src/pytest_docker/init.py#L169-L175 it seems that when fallback returns True but docker ps still works successfully it happily goes on and still ends up doing L178, spinning up the containers through compose. It seems that only when fallback is True and docker ps fails it does what I would expect.

    This seems very strange to me. If I say it's supposed to use the fallback, why still check if docker is available and then ignore what the user configured?

    I would expect the code like this instead:

    
        if docker_allow_fallback is True:
                # Run against localhost
                yield Services(docker_compose, docker_allow_fallback=True)
                return
    
    opened by daenney 5
  • Allow fixture to return multiple compose files

    Allow fixture to return multiple compose files

    As described in https://docs.docker.com/compose/extends/, docker compose can be passed multiple compose files that are then merged. This change allows the docker_compose_file fixture to make use of this by returning e.g. a list or a tuple.

    opened by gdetrez 5
  • Remove attrs constraint

    Remove attrs constraint

    attrs is required by pytest but we should be able to remove the <22 constraint since it isn't directly used by this package

    pytest-docker==1.0.0
      - pytest [required: >=4.0,<8.0, installed: 7.1.2]
        - attrs [required: >=19.2.0, installed: 22.1.0]
        - iniconfig [required: Any, installed: 1.1.1]
        - packaging [required: Any, installed: 21.3]
          - pyparsing [required: >=2.0.2,!=3.0.5, installed: 3.0.9]
        - pluggy [required: >=0.12,<2.0, installed: 1.0.0]
        - py [required: >=1.8.2, installed: 1.11.0]
        - tomli [required: >=1.0.0, installed: 2.0.1]
    pytest-pycodestyle==2.3.0
      - pycodestyle [required: Any, installed: 2.9.1]
      - pytest [required: >=7.0, installed: 7.1.2]
        - attrs [required: >=19.2.0, installed: 22.1.0]
        - iniconfig [required: Any, installed: 1.1.1]
        - packaging [required: Any, installed: 21.3]
          - pyparsing [required: >=2.0.2,!=3.0.5, installed: 3.0.9]
        - pluggy [required: >=0.12,<2.0, installed: 1.0.0]
        - py [required: >=1.8.2, installed: 1.11.0]
        - tomli [required: >=1.0.0, installed: 2.0.1]
    pytest-pylint==0.18.0
      - pylint [required: >=2.3.0, installed: 2.14.5]
        - astroid [required: >=2.11.6,<=2.12.0-dev0, installed: 2.11.7]
          - lazy-object-proxy [required: >=1.4.0, installed: 1.7.1]
          - setuptools [required: >=20.0, installed: 58.1.0]
          - wrapt [required: >=1.11,<2, installed: 1.14.1]
        - dill [required: >=0.2, installed: 0.3.5.1]
        - isort [required: >=4.2.5,<6, installed: 5.10.1]
        - mccabe [required: >=0.6,<0.8, installed: 0.7.0]
        - platformdirs [required: >=2.2.0, installed: 2.5.2]
        - tomli [required: >=1.1.0, installed: 2.0.1]
        - tomlkit [required: >=0.10.1, installed: 0.11.1]
      - pytest [required: >=5.4, installed: 7.1.2]
        - attrs [required: >=19.2.0, installed: 22.1.0]
    
    
    opened by Ryan-Bell 4
  • Allow for overriding of pytest fixture scope

    Allow for overriding of pytest fixture scope

    Being able to override the pytest fixtures to have a "function" scope instead of "session" scope would be useful when using containers that may carry state between tests.

    An example of a useful configuration (though hard-coded): https://github.com/Greenlight-Analytical/pytest-docker/commit/102eedb65d3ff8a9cddc285363bd31d6e8e45f15

    Some boilerplate to connect a flag:

    def pytest_addoption(parser):
        parser.addoption(
            "--keep-containers",
            action="store_true",
            help="Keep containers between tests. This is faster but may cause test failures from leftover state between tests.",
        )
    
    
    @pytest.fixture
    def keep_containers(request):
        return request.config.getoption("--keep-containers")
    
    
    def keep_containers_scope(fixture_name, config):
        if config.getoption("--keep-containers", None):
            return "session"
        return "function"
    
    
    @pytest.fixture(scope=keep_containers_scope)
    # This annotation can apply to all fixtures, instead of the hard-coded "session" scope
    
    opened by austinkeller 0
  • feat: raise a more specific exception on timeout

    feat: raise a more specific exception on timeout

    This provides a narrower exception to catch when calling wait_until_responsive.

    Context: when an entire testsuite relies on a service, I'd like to call pytest.exit() if it times out so that a useful error message is displayed for the user instead of failing the fixture setup for all tests and producing a wall of text.

    /cc @Luminaar

    opened by nejch 0
  • Insufficient removal of control characters on windows with cmd.exe as shell

    Insufficient removal of control characters on windows with cmd.exe as shell

    Affected Line https://github.com/avast/pytest-docker/blob/567fa091a0d8ccf4ac2e8897c1ccecf08774125c/src/pytest_docker/plugin.py#L79 endpoint = output.strip().decode("utf-8") Affected Version: at least since 0.10.3

    I use PyCharm on Windows 10 to debug my pytest test cases. Starting the conatiners the above line is executed to to resolve a port for a service.

    output = self._docker_compose.execute("port %s %d" % (service, container_port)) results to b'0.0.0.0:12347\r\n\x1b[0m'. No control character is removed. Hence, output.strip().decode("utf-8") result to s string including the control characters.

    Some lines later (L86) if len(endpoint.split("\n")) > 1: does not support windows line endings.

    On Linux or Git Bash for Windows, there is no problem.

    My fix for that. Please check this solution:

    endpoint = endpoint.replace("\r",'') # add support for windows line ending
    if len(endpoint.split("\n")) > 1:
          endpoint = endpoint.split("\n")[-1] # index -1 is also wrong for me because it results to '\x1b[0m'. Maybe use 0 or -2
    
    opened by mf01 2
  • Endpoint empty error

    Endpoint empty error

    On a windows system, the endpoint command returns a weird string:

    0.0.0.0:9997\r\n

    That's why the strip here does not change the string, and this line takes the garbage part. I have solved it by using a regexp:

    ips = re.findall(r'[0-9]+(?:\.[0-9]+){3}:[0-9]+', endpoint)
    assert len(ips) == 1
    endpoint = ips[0]
    

    Do you think this is a possible solution that could be merged in?

    opened by didacrarbones 5
  • Docs for multiple docker-compose files

    Docs for multiple docker-compose files

    pytest-docker already supports shared compose files. However I couldn't find it in the docs. Is it really missing? If so, can I send a PR to add something to the README?

    opened by edinhodiluviano 4
Releases(0.12.0)
Owner
Avast
https://avast.github.io
Avast
A set of pytest fixtures to test Flask applications

pytest-flask An extension of pytest test runner which provides a set of useful tools to simplify testing and development of the Flask extensions and a

pytest-dev 433 Dec 23, 2022
A collection of testing examples using pytest and many other libreris

Effective testing with Python This project was created for PyConEs 2021 Check out the test samples at tests Check out the slides at slides (markdown o

Héctor Canto 10 Oct 23, 2022
Akulaku Create NewProduct Automation using Selenium Python

Akulaku-Create-NewProduct-Automation Akulaku Create NewProduct Automation using Selenium Python Usage: 1. Install Python 3.9 2. Open CMD on Bot Folde

Rahul Joshua Damanik 1 Nov 22, 2021
A framework-agnostic library for testing ASGI web applications

async-asgi-testclient Async ASGI TestClient is a library for testing web applications that implements ASGI specification (version 2 and 3). The motiva

122 Nov 22, 2022
User-interest mock backend server implemnted using flask restful, and SQLAlchemy ORM confiugred with sqlite

Flask_Restful_SQLAlchemy_server User-interest mock backend server implemnted using flask restful, and SQLAlchemy ORM confiugred with sqlite. Backend b

Austin Weigel 1 Nov 17, 2022
PyAutoEasy is a extension / wrapper around the famous PyAutoGUI, a cross-platform GUI automation tool to replace your boooring repetitive tasks.

PyAutoEasy PyAutoEasy is a extension / wrapper around the famous PyAutoGUI, a cross-platform GUI automation tool to replace your boooring repetitive t

Dingu Sagar 7 Oct 27, 2022
Silky smooth profiling for Django

Silk Silk is a live profiling and inspection tool for the Django framework. Silk intercepts and stores HTTP requests and database queries before prese

Jazzband 3.7k Jan 04, 2023
DUCKSPLOIT - Windows Hacking FrameWork using Reverse Shell

Ducksploit Install Ducksploit Hacker setup raspberry pico Download https://githu

2 Jan 31, 2022
A pytest plugin to run an ansible collection's unit tests with pytest.

pytest-ansible-units An experimental pytest plugin to run an ansible collection's unit tests with pytest. Description pytest-ansible-units is a pytest

Community managed Ansible repositories 9 Dec 09, 2022
AllPairs is an open source test combinations generator written in Python

AllPairs is an open source test combinations generator written in Python

Robson Agapito Correa 5 Mar 05, 2022
模仿 USTC CAS 的程序,用于开发校内网站应用的本地调试。

ustc-cas-mock 模仿 USTC CAS 的程序,用于开发校内网站应用阶段调试。 请勿在生产环境部署! 只测试了最常用的三个 CAS route: /login /serviceValidate(验证 CAS ticket) /logout 没有测试过 proxy ticket。(因为我

taoky 4 Jan 27, 2022
A Proof of concept of a modern python CLI with click, pydantic, rich and anyio

httpcli This project is a proof of concept of a modern python networking cli which can be simple and easy to maintain using some of the best packages

Kevin Tewouda 17 Nov 15, 2022
A rewrite of Python's builtin doctest module (with pytest plugin integration) but without all the weirdness

The xdoctest package is a re-write of Python's builtin doctest module. It replaces the old regex-based parser with a new abstract-syntax-tree based pa

Jon Crall 174 Dec 16, 2022
a socket mock framework - for all kinds of socket animals, web-clients included

mocket /mɔˈkɛt/ A socket mock framework for all kinds of socket animals, web-clients included - with gevent/asyncio/SSL support ...and then MicroPytho

Giorgio Salluzzo 249 Dec 14, 2022
Donors data of Tamil Nadu Chief Ministers Relief Fund scrapped from https://ereceipt.tn.gov.in/cmprf/Interface/CMPRF/MonthWiseReport

Tamil Nadu Chief Minister's Relief Fund Donors Scrapped data from https://ereceipt.tn.gov.in/cmprf/Interface/CMPRF/MonthWiseReport Scrapper scrapper.p

Arunmozhi 5 May 18, 2021
A pure Python script to easily get a reverse shell

easy-shell A pure Python script to easily get a reverse shell. How it works? After sending a request, it generates a payload with different commands a

Cristian Souza 48 Dec 12, 2022
pytest plugin providing a function to check if pytest is running.

pytest-is-running pytest plugin providing a function to check if pytest is running. Installation Install with: python -m pip install pytest-is-running

Adam Johnson 21 Nov 01, 2022
Divide full port scan results and use it for targeted Nmap runs

Divide Et Impera And Scan (and also merge the scan results) DivideAndScan is used to efficiently automate port scanning routine by splitting it into 3

snovvcrash 226 Dec 30, 2022
Automated mouse clicker script using PyAutoGUI and Typer.

clickpy Automated mouse clicker script using PyAutoGUI and Typer. This app will randomly click your mouse between 1 second and 3 minutes, to prevent y

Joe Fitzgibbons 0 Dec 01, 2021
A Simple Unit Test Matcher Library for Python 3

pychoir - Python Test Matchers for humans Super duper low cognitive overhead matching for Python developers reading or writing tests. Implemented in p

Antti Kajander 15 Sep 14, 2022