How to unit test function that requires an active Click context in Python
Asked Answered
E

2

7

I am working on a command-line shell, and I'm trying to test some functions that parse command arguments.

parser.py:

import shlex
import click


def process_cmd(args, called_self=False):
    result = args

    # Replace aliases
    if not called_self:
        aliases = click.get_current_context().obj.config['ALIASES']
        if result[0] in aliases:
            substitution = process_cmd(split_args(aliases[result[0]]), True)
            if len(result) == 1:
                result = substitution
            else:
                result = substitution + result[1:]

    return result


def split_pipeline(args):
    cmd = []
    for arg in args:
        if arg == '|':
            yield process_cmd(cmd)
            cmd = []
        else:
            cmd.append(arg)
    # yield the last part of the pipeline
    yield process_cmd(cmd)

This is a unit test I wrote for split_pipeline:

import parser

def test_pipeline():
    args = ['1', '2', '|', '3', '|', '4']
    result = [i for i in parser.split_pipeline(args)]
    assert result == [['1', '2'], ['3'], ['4']]

When this unit test is run, I get an error saying that there is no active Click context.

Elliellicott answered 28/11, 2019 at 20:53 Comment(0)
S
7

The click library Context() object can be used as a Python context. So to set an active context in a test, you can simply do:

with ctx:
    ....

To create a Context to test with, you can instantiate one like:

ctx = click.Context(a_click_command, obj=my_context_object)

Test Code:

import click


def process_cmd():
    click.echo(click.get_current_context().obj['prop'])


def test_with_context():
    ctx = click.Context(click.Command('cmd'), obj={'prop': 'A Context'})
    with ctx:
        process_cmd()

test_with_context()

Results:

A Context
Shroud answered 29/11, 2019 at 15:0 Comment(1)
I made a pytest fixture which returns a Click contextElliellicott
D
0

For those using mocks, it is possible to also push a Mock context using the following:

from unittest.mock import MagicMock
from click.globals import push_context

def test_foo():
    ctx = MagicMock()
    push_context(ctx)
    
    # subsequent calls will use the mocked ctx
Discotheque answered 7/5 at 7:14 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.