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
Test python asyncio-based code with ease.

aiounittest Info The aiounittest is a helper library to ease of your pain (and boilerplate), when writing a test of the asynchronous code (asyncio). Y

Krzysztof Warunek 55 Oct 30, 2022
MongoDB panel for the Flask Debug Toolbar

Flask Debug Toolbar MongoDB Panel Info: An extension panel for Rob Hudson's Django Debug Toolbar that adds MongoDB debugging information Author: Harry

Cenk Altı 4 Dec 11, 2019
XSSearch - A comprehensive reflected XSS tool built on selenium framework in python

XSSearch A Comprehensive Reflected XSS Scanner XSSearch is a comprehensive refle

Sathyaprakash Sahoo 49 Oct 18, 2022
A simple asynchronous TCP/IP Connect Port Scanner in Python 3

Python 3 Asynchronous TCP/IP Connect Port Scanner A simple pure-Python TCP Connect port scanner. This application leverages the use of Python's Standa

70 Jan 03, 2023
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
Enabling easy statistical significance testing for deep neural networks.

deep-significance: Easy and Better Significance Testing for Deep Neural Networks Contents ⁉️ Why 📥 Installation 🔖 Examples Intermezzo: Almost Stocha

Dennis Ulmer 270 Dec 20, 2022
The pytest framework makes it easy to write small tests, yet scales to support complex functional testing

The pytest framework makes it easy to write small tests, yet scales to support complex functional testing for applications and libraries. An example o

pytest-dev 9.6k Jan 02, 2023
:game_die: Pytest plugin to randomly order tests and control random.seed

pytest-randomly Pytest plugin to randomly order tests and control random.seed. Features All of these features are on by default but can be disabled wi

pytest-dev 471 Dec 30, 2022
Just for testing video streaming using pytgcalls.

tgvc-video-tests Just for testing video streaming using pytgcalls. Note: The features used in this repository is highly experimental and you might not

wrench 34 Dec 27, 2022
Implement unittest, removing all global variable and returning values

Implement unittest, removing all global variable and returning values

Placide 1 Nov 01, 2021
hyppo is an open-source software package for multivariate hypothesis testing.

hyppo (HYPothesis Testing in PythOn, pronounced "Hippo") is an open-source software package for multivariate hypothesis testing.

neurodata 137 Dec 18, 2022
Repository for JIDA SNP Browser Web Application: Local Deployment

JIDA JIDA is a web application that retrieves SNP information for a genomic region of interest in Homo sapiens and calculates specific summary statist

3 Mar 03, 2022
Tools for test driven data-wrangling and data validation.

datatest: Test driven data-wrangling and data validation Datatest helps to speed up and formalize data-wrangling and data validation tasks. It impleme

269 Dec 16, 2022
Switch among Guest VMs organized by Resource Pool

Proxmox PCI Switcher Switch among Guest VMs organized by Resource Pool. main features: ONE GPU card, N OS (at once) Guest VM command client Handler po

Rosiney Gomes Pereira 111 Dec 27, 2022
Find index entries in $INDEX_ALLOCATION attributes

INDXRipper Find index entries in $INDEX_ALLOCATION attributes Timeline created using mactime.pl on the combined output of INDXRipper and fls. See: sle

32 Nov 05, 2022
Rerun pytest when your code changes

A simple watcher for pytest Overview pytest-watcher is a tool to automatically rerun pytest when your code changes. It looks for the following events:

Olzhas Arystanov 74 Dec 29, 2022
Show coverage stats online via coveralls.io

Coveralls for Python Test Status: Version Info: Compatibility: Misc: coveralls.io is a service for publishing your coverage stats online. This package

Kevin James 499 Dec 28, 2022
Web testing library for Robot Framework

SeleniumLibrary Contents Introduction Keyword Documentation Installation Browser drivers Usage Extending SeleniumLibrary Community Versions History In

Robot Framework 1.2k Jan 03, 2023
Airspeed Velocity: A simple Python benchmarking tool with web-based reporting

airspeed velocity airspeed velocity (asv) is a tool for benchmarking Python packages over their lifetime. It is primarily designed to benchmark a sing

745 Dec 28, 2022
pytest plugin for distributed testing and loop-on-failures testing modes.

xdist: pytest distributed testing plugin The pytest-xdist plugin extends pytest with some unique test execution modes: test run parallelization: if yo

pytest-dev 1.1k Dec 30, 2022