Skip to content

then_enter_with(None) makes the stub return None instead of an enterable context manager #135

@SyntaxColoring

Description

@SyntaxColoring

If you have production code that looks like this:

with foo.bar(bar_arg) as baz:
    ...

Then its Decoy test might do:

bar_arg_captor = matchers.Captor()
decoy.when(foo.bar(bar_arg_captor())).then_enter_with("baz value")

But sometimes the with statement has no as clause:

with foo.bar(bar_arg):
    ...

I expected this to work:

bar_arg_captor = matchers.Captor()
decoy.when(foo.bar(bar_arg_captor())).then_enter_with(None)

But this causes the production code's call to foo.bar(bar_arg) to return None. It should instead return a context manager that returns None when it's entered.

It looks to me like this is an unintentional side-effect of the way StubBehavior works internally:

class StubBehavior(NamedTuple):
"""A recorded stub behavior."""
return_value: Optional[Any] = None
context_value: Optional[Any] = None
error: Optional[Exception] = None
action: Optional[Callable[..., Any]] = None
once: bool = False

It can't distinguish between being configured with a context_value that's None versus not being configured with a context_value at all.

To solve a similar problem, the implementation of dataclasses uses a special MISSING sentinel value that's distinct from None. Maybe a similar pattern could work here.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions