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
模仿 USTC CAS 的程序,用于开发校内网站应用的本地调试。

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

taoky 4 Jan 27, 2022
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
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
Python package to easily work with selenium and manage tabs effectively.

Simple Selenium The aim of this package is to quickly get started with working with selenium for simple browser automation tasks. Installation Install

Vishal Kumar Mishra 1 Oct 27, 2021
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 Django plugin for pytest.

Welcome to pytest-django! pytest-django allows you to test your Django project/applications with the pytest testing tool. Quick start / tutorial Chang

pytest-dev 1.1k Dec 31, 2022
Thin-wrapper around the mock package for easier use with pytest

pytest-mock This plugin provides a mocker fixture which is a thin-wrapper around the patching API provided by the mock package: import os class UnixF

pytest-dev 1.5k Jan 05, 2023
CNE-OVS-SIT - OVS System Integration Test Suite

CNE-OVS-SIT - OVS System Integration Test Suite Introduction User guide Discussion Introduction CNE-OVS-SIT is a test suite for OVS end-to-end functio

4 Jan 09, 2022
0hh1 solver for the web (selenium) and also for mobile (adb)

0hh1 - Solver Aims to solve the '0hh1 puzzle' for all the sizes (4x4, 6x6, 8x8, 10x10 12x12). for both the web version (using selenium) and on android

Adwaith Rajesh 1 Nov 05, 2021
Python Moonlight (Machine Learning) Practice

PyML Python Moonlight (Machine Learning) Practice Contents Design Documentation Prerequisites Checklist Dev Setup Testing Run Prerequisites Python 3 P

Dockerian Seattle 2 Dec 25, 2022
a plugin for py.test that changes the default look and feel of py.test (e.g. progressbar, show tests that fail instantly)

pytest-sugar pytest-sugar is a plugin for pytest that shows failures and errors instantly and shows a progress bar. Requirements You will need the fol

Teemu 963 Dec 28, 2022
Simple assertion library for unit testing in python with a fluent API

assertpy Simple assertions library for unit testing in Python with a nice fluent API. Supports both Python 2 and 3. Usage Just import the assert_that

19 Sep 10, 2022
Public repo for automation scripts

Script_Quickies Public repo for automation scripts Dependencies Chrome webdriver .exe (make sure it matches the version of chrome you are using) Selen

CHR-onicles 1 Nov 04, 2021
Run ISP speed tests and save results

SpeedMon Automatically run periodic internet speed tests and save results to a variety of storage backends. Supported Backends InfluxDB v1 InfluxDB v2

Matthew Carey 9 May 08, 2022
Python tools for penetration testing

pyTools_PT python tools for penetration testing Please don't use these tool for illegal purposes. These tools is meant for penetration testing for leg

Gourab 1 Dec 01, 2021
Parameterized testing with any Python test framework

Parameterized testing with any Python test framework Parameterized testing in Python sucks. parameterized fixes that. For everything. Parameterized te

David Wolever 714 Dec 21, 2022
Fi - A simple Python 3.9+ command-line application for managing Fidelity portfolios

fi fi is a simple Python 3.9+ command-line application for managing Fidelity por

Darik Harter 2 Feb 26, 2022
A folder automation made using Watch-dog, it only works in linux for now but I assume, it will be adaptable to mac and PC as well

folder-automation A folder automation made using Watch-dog, it only works in linux for now but I assume, it will be adaptable to mac and PC as well Th

Parag Jyoti Paul 31 May 28, 2021
Hamcrest matchers for Python

PyHamcrest Introduction PyHamcrest is a framework for writing matcher objects, allowing you to declaratively define "match" rules. There are a number

Hamcrest 684 Dec 29, 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