ClearcodeHQ / pytest-mongo

This is a pytest plugin, that enables you to test your code that relies on a running MongoDB database. It allows you to specify fixtures for MongoDB process and client.
https://pypi.python.org/pypi/pytest-mongo/
GNU Lesser General Public License v3.0
22 stars 5 forks source link

Allow defining port (and maybe other configuration) at runtime #308

Open guludo opened 3 years ago

guludo commented 3 years ago

Hi there,

I have a use case where I will only know the port to a running instance of mongo at runtime (and I'm able to get that via a session-scoped fixture). I would like to be able to tell pytest-mongo to use such a port. By looking at the documentation, it seems that there isn't a supported way of passing the port (or other configuration) to pytest-mongo at runtime. That would really be useful.

A way I can think of providing such a feature is to allow users to override a fixture that will give the configuration necessary. For example:

@pyest.fixture(scope='session')
def pytest_mongo_conf(my, own, required, fixtures, here):
    ... # Get host and port from other fixtures
    # Options defined in the returned dictionary would override their previous (or default) values
    return {
        'host': host,
        'port': port,
    }
guludo commented 3 years ago

Just a quick note: I was able to get away with the following workaround: overriding the mongo_noproc fixture to mimic what the original source code does:

@pytest.fixture(scope='session')
def mongo_noproc(mongo_service):
    yield pytest_mongo.executor_noop.NoopExecutor(
        host=mongo_service['host'],
        port=mongo_service['port'],
    )

However, I see that as a workaround and I believe that having official way of configuring pytest-mongo at runtime would be a better solution.

fizyk commented 3 years ago

🤔 how do you start the MongoDB for tests then? Do you have any snippet you could show me your use case here?

guludo commented 3 years ago

Do you have any snippet you could show me your use case here?

Sure! Here is a fragment of my current setup:

@pytest.fixture(scope='session')
def mongo_service():
    """
    Fixture that starts a mongo service and yields useful information in a
    dictionary.
    """
    container_id = subprocess.run(
        ['docker', 'run', '--rm', '-d', '-p', '27017', 'mongo:4'],
        check=True,
        stdout=subprocess.PIPE,
        text=True,
    ).stdout.strip()

    try:
        # Get published port
        port_lines = subprocess.run(
            ['docker', 'container', 'port', container_id, '27017/tcp'],
            stdout=subprocess.PIPE,
            text=True,
        ).stdout.splitlines()

        ip, port = port_lines[0].rsplit(':')

        info = {
            'exec_prefix': ['docker', 'exec', '-i', container_id],
            'host': ip,
            'port': int(port),
        }

        yield info
    finally:
        subprocess.run(
            ['docker', 'kill', container_id],
            check=True,
            stdout=subprocess.DEVNULL,
        )

@pytest.fixture(scope='session')
def mongo_noproc(mongo_service):
    yield pytest_mongo.executor_noop.NoopExecutor(
        host=mongo_service['host'],
        port=mongo_service['port'],
    )

What I think would be a nicer solution:

@pytest.fixture(scope='session')
def mongo_service():
   ... # Same as before

@pyest.fixture(scope='session')
def pytest_mongo_conf(mongo_service):
    return {
        'host': mongo_service['host'],
        'port': mongo_service['port'],
    }
fizyk commented 3 years ago

Okay, had to check how does it the -p and the container port work actually. I usually run tests within docker-compose setup in this case - container with code and mongo, they share network between them. Or a CI pipeline, like github actions or bitbucket pipelines, that separate it tests, and I can have same, defined test port there and use it in configuration.

Here, I think the simplest and to be honest, the most elegant solution would be what you did, if you use that info from mongo_service anywhere else, or merge both within one fixture returning the NoopExecutor instead of info. It has both host and port attributes on itself, so you can use it as well anywhere you need host and port informations.

guludo commented 3 years ago

Here, I think the simplest and to be honest, the most elegant solution would be what you did, if you use that info from mongo_service anywhere else, or merge both within one fixture returning the NoopExecutor instead of info. It has both host and port attributes on itself, so you can use it as well anywhere you need host and port informations.

Great! Thanks. So I'll keep my code as is.

Would it be possible to have a note in the documentation about the possibility of overriding mongo_noproc to return a instance of pytest_mongo.executor_noop.NoopExecutor? Currently, it feels like I'm using an implementation detail that might change in the future.