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
输入Google Hacking语句,自动调用Chrome浏览器爬取结果

Google-Hacking-Crawler 该脚本可输入Google Hacking语句,自动调用Chrome浏览器爬取结果 环境配置 python -m pip install -r requirements.txt 下载Chrome浏览器

Jarcis 4 Jun 21, 2022
A Python program that will log into your scheduled Google Meets hands free

Chrome GMautomation General Information This Python program will open up Chrome and log into your scheduled Google Meet with camera and mic turned off

Jonathan Leow 5 Dec 31, 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
A pytest plugin that enables you to test your code that relies on a running Elasticsearch search engine

pytest-elasticsearch What is this? This is a pytest plugin that enables you to test your code that relies on a running Elasticsearch search engine. It

Clearcode 65 Nov 10, 2022
a wrapper around pytest for executing tests to look for test flakiness and runtime regression

bubblewrap a wrapper around pytest for assessing flakiness and runtime regressions a cs implementations practice project How to Run: First, install de

Anna Nagy 1 Aug 05, 2021
tidevice can be used to communicate with iPhone device

tidevice can be used to communicate with iPhone device

Alibaba 1.8k Jan 08, 2023
fsociety Hacking Tools Pack – A Penetration Testing Framework

Fsociety Hacking Tools Pack A Penetration Testing Framework, you will have every script that a hacker needs. Works with Python 2. For a Python 3 versi

Manisso 8.2k Jan 03, 2023
Python Testing Crawler 🐍 🩺 🕷️ A crawler for automated functional testing of a web application

Python Testing Crawler 🐍 🩺 🕷️ A crawler for automated functional testing of a web application Crawling a server-side-rendered web application is a

70 Aug 07, 2022
The best, free, all in one, multichecking, pentesting utility

The best, free, all in one, multichecking, pentesting utility

Mickey 58 Jan 03, 2023
Python scripts for a generic performance testing infrastructure using Locust.

TODOs Reference to published paper or online version of it loadtest_plotter.py: Cleanup and reading data from files ARS_simulation.py: Cleanup, docume

Juri Tomak 3 Dec 15, 2022
Bayesian A/B testing

bayesian_testing is a small package for a quick evaluation of A/B (or A/B/C/...) tests using Bayesian approach.

Matus Baniar 35 Dec 15, 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
Custom Selenium Chromedriver | Zero-Config | Passes ALL bot mitigation systems (like Distil / Imperva/ Datadadome / CloudFlare IUAM)

Custom Selenium Chromedriver | Zero-Config | Passes ALL bot mitigation systems (like Distil / Imperva/ Datadadome / CloudFlare IUAM)

Leon 3.5k Dec 30, 2022
Django-google-optimize is a Django application designed to make running server side Google Optimize A/B tests easy.

Django-google-optimize Django-google-optimize is a Django application designed to make running Google Optimize A/B tests easy. Here is a tutorial on t

Adin Hodovic 39 Oct 25, 2022
Obsei is a low code AI powered automation tool.

Obsei is a low code AI powered automation tool. It can be used in various business flows like social listening, AI based alerting, brand image analysis, comparative study and more .

Obsei 782 Dec 31, 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
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
A pytest plugin to skip `@pytest.mark.slow` tests by default.

pytest-skip-slow A pytest plugin to skip @pytest.mark.slow tests by default. Include the slow tests with --slow. Installation $ pip install pytest-ski

Brian Okken 19 Jan 04, 2023
A complete test automation tool

Golem - Test Automation Golem is a test framework and a complete tool for browser automation. Tests can be written with code in Python, codeless using

486 Dec 30, 2022
Connexion-faker - Auto-generate mocks from your Connexion API using OpenAPI

Connexion Faker Get Started Install With poetry: poetry add connexion-faker # a

Erle Carrara 6 Dec 19, 2022