Skip to content

Commit 4058c80

Browse files
committed
Closes #673
1 parent 855dc09 commit 4058c80

File tree

6 files changed

+111
-16
lines changed

6 files changed

+111
-16
lines changed

docs/pages/contrib/pytest_plugins.rst

Lines changed: 53 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,43 @@ To use it in your tests, request ``returns`` fixture like so:
3131
...
3232
3333
34+
assert_equal
35+
~~~~~~~~~~~~
36+
37+
We have a special helper to compare containers' equality.
38+
39+
It might be an easy task for two ``Result`` or ``Maybe`` containers,
40+
but it is not very easy for two ``ReaderResult`` or ``FutureResult`` instances.
41+
42+
Take a look:
43+
44+
.. code:: python
45+
46+
>>> from returns.result import Result
47+
>>> from returns.context import Reader
48+
49+
>>> assert Result.from_value(1) == Result.from_value(1)
50+
>>> Reader.from_value(1) == Reader.from_value(1)
51+
False
52+
53+
So, we can use :func:`~returns.primitives.asserts.assert_equal`
54+
or ``returns.assert_equal`` method from our ``pytest`` fixture:
55+
56+
.. code:: python
57+
58+
>>> from returns.result import Success
59+
>>> from returns.context import Reader
60+
>>> from returns.contrib.pytest import ReturnsAsserts
61+
62+
>>> def test_container_equality(returns: ReturnsAsserts):
63+
... returns.assert_equal(Success(1), Success(1))
64+
... returns.assert_equal(Reader.from_value(1), Reader.from_value(1))
65+
66+
>>> # We only run these tests manually, because it is a doc example:
67+
>>> returns_fixture = getfixture('returns')
68+
>>> test_container_equality(returns_fixture)
69+
70+
3471
is_error_handled
3572
~~~~~~~~~~~~~~~~
3673

@@ -39,14 +76,18 @@ It tests that containers do handle error track.
3976

4077
.. code:: python
4178
42-
from returns.result import Failure, Success
43-
from returns.contrib.pytest import ReturnsAsserts
79+
>>> from returns.result import Failure, Success
80+
>>> from returns.contrib.pytest import ReturnsAsserts
81+
82+
>>> def test_error_handled(returns: ReturnsAsserts):
83+
... assert not returns.is_error_handled(Failure(1))
84+
... assert returns.is_error_handled(
85+
... Failure(1).lash(lambda _: Success('default value')),
86+
... )
4487
45-
def test_my_container(returns: ReturnsAsserts):
46-
assert not returns.is_error_handled(Failure(1))
47-
assert returns.is_error_handled(
48-
Failure(1).lash(lambda _: Success('default value')),
49-
)
88+
>>> # We only run these tests manually, because it is a doc example:
89+
>>> returns_fixture = getfixture('returns')
90+
>>> test_error_handled(returns_fixture)
5091
5192
We recommed to unit test big chunks of code this way.
5293
This is helpful for big pipelines where
@@ -82,8 +123,6 @@ created and looking for the desired function.
82123
>>> from returns.result import Result, Success, Failure
83124
>>> from returns.contrib.pytest import ReturnsAsserts
84125
85-
>>> returns_fixture = getfixture('returns')
86-
87126
>>> def desired_function(arg: str) -> Result[int, str]:
88127
... if arg.isnumeric():
89128
... return Success(int(arg))
@@ -101,6 +140,8 @@ created and looking for the desired function.
101140
... with returns.has_trace(Success, desired_function):
102141
... Success('42').bind(desired_function)
103142
143+
>>> # We only run these tests manually, because it is a doc example:
144+
>>> returns_fixture = getfixture('returns')
104145
>>> test_if_failure_is_created_at_convert_function(returns_fixture)
105146
>>> test_if_success_is_created_at_convert_function(returns_fixture)
106147
@@ -128,3 +169,6 @@ API Reference
128169

129170
.. automodule:: returns.contrib.pytest.plugin
130171
:members:
172+
173+
.. automodule:: returns.primitives.asserts
174+
:members:

returns/contrib/pytest/plugin.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,18 @@ class ReturnsAsserts(object):
3333

3434
__slots__ = ()
3535

36+
def assert_equal(
37+
self,
38+
first,
39+
second,
40+
*,
41+
deps=None,
42+
backend: str = 'asyncio',
43+
) -> None:
44+
"""Can compare two containers even with extra calling and awaiting."""
45+
from returns.primitives.asserts import assert_equal
46+
assert_equal(first, second, deps=deps, backend=backend)
47+
3648
def is_error_handled(self, container: 'FailableN') -> bool:
3749
"""Ensures that container has its error handled in the end."""
3850
return bool(getattr(container, _ERROR_FIELD, False))

returns/primitives/asserts.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,7 @@ def _convert(container, *, deps, backend: str):
4141
deps=deps,
4242
backend=backend,
4343
)
44-
# TODO: remove `callable`
45-
if callable(container) or isinstance(container, reader.Contextable):
44+
elif isinstance(container, reader.Contextable):
4645
return _convert(
4746
container(deps),
4847
deps=deps,

tests/test_contrib/test_pytest/test_plugin_error_handler.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
RequiresContextIOResult,
66
RequiresContextResult,
77
)
8+
from returns.contrib.pytest import ReturnsAsserts
89
from returns.functions import identity
910
from returns.future import FutureResult
1011
from returns.io import IOFailure, IOSuccess
@@ -38,7 +39,7 @@ def _under_test(
3839
@pytest.mark.parametrize('kwargs', [
3940
{'should_lash': True},
4041
])
41-
def test_error_handled(container, returns, kwargs):
42+
def test_error_handled(returns: ReturnsAsserts, container, kwargs):
4243
"""Demo on how to use ``pytest`` helpers to work with error handling."""
4344
error_handled = _under_test(container, **kwargs)
4445

@@ -59,7 +60,7 @@ def test_error_handled(container, returns, kwargs):
5960
RequiresContextResult.from_value(1),
6061
RequiresContextResult.from_failure(1),
6162
])
62-
def test_error_not_handled(container, returns):
63+
def test_error_not_handled(returns: ReturnsAsserts, container):
6364
"""Demo on how to use ``pytest`` helpers to work with error handling."""
6465
error_handled = _under_test(container)
6566

@@ -76,7 +77,7 @@ def test_error_not_handled(container, returns):
7677
RequiresContextFutureResult.from_value(1),
7778
RequiresContextFutureResult.from_failure(1),
7879
])
79-
async def test_error_not_handled_async(container, returns):
80+
async def test_error_not_handled_async(returns: ReturnsAsserts, container):
8081
"""Demo on how to use ``pytest`` helpers to work with error handling."""
8182
error_handled = _under_test(container)
8283

tests/test_primitives/test_asserts/test_assert_equal.py

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
ReaderIOResult,
99
ReaderResult,
1010
)
11+
from returns.contrib.pytest import ReturnsAsserts
1112
from returns.future import Future, FutureResult
1213
from returns.io import IO, IOResult
1314
from returns.maybe import Maybe
@@ -42,17 +43,42 @@
4243

4344

4445
@pytest.mark.parametrize('container', _containers)
45-
def test_assert_equal(container, anyio_backend_name):
46+
def test_assert_equal(container, anyio_backend_name: str):
4647
"""Ensure that containers can be equal."""
4748
assert_equal(container, container, backend=anyio_backend_name)
4849

4950

5051
@pytest.mark.parametrize('container', _containers)
51-
def test_assert_equal_not(container, anyio_backend_name):
52+
def test_assert_equal_plugin(
53+
container,
54+
anyio_backend_name: str,
55+
returns: ReturnsAsserts,
56+
):
57+
"""Ensure that containers can be equal."""
58+
returns.assert_equal(container, container, backend=anyio_backend_name)
59+
60+
61+
@pytest.mark.parametrize('container', _containers)
62+
def test_assert_equal_not(container, anyio_backend_name: str):
5263
"""Ensure that containers can be not equal."""
5364
with pytest.raises(AssertionError):
5465
assert_equal(
5566
container,
5667
container.from_value(2),
5768
backend=anyio_backend_name,
5869
)
70+
71+
72+
@pytest.mark.parametrize('container', _containers)
73+
def test_assert_equal_not_plugin(
74+
container,
75+
anyio_backend_name: str,
76+
returns: ReturnsAsserts,
77+
):
78+
"""Ensure that containers can be not equal."""
79+
with pytest.raises(AssertionError):
80+
returns.assert_equal(
81+
container,
82+
container.from_value(2),
83+
backend=anyio_backend_name,
84+
)
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
- case: check_all_laws
2+
disable_cache: false
3+
main: |
4+
from returns.contrib.pytest import ReturnsAsserts
5+
6+
x: ReturnsAsserts
7+
reveal_type(x.has_trace)
8+
reveal_type(x.is_error_handled)
9+
reveal_type(x.assert_equal)
10+
out: |
11+
main:4: note: Revealed type is 'def [_ReturnsResultType <: def (*Any, **Any) -> returns.interfaces.specific.result.ResultLikeN[Any, Any, Any], _FunctionType <: def (*Any, **Any) -> Any] (trace_type: _ReturnsResultType`1429, function_to_search: _FunctionType`1430) -> contextlib._GeneratorContextManager[None]'
12+
main:5: note: Revealed type is 'def (container: returns.interfaces.failable.FailableN[Any, Any, Any]) -> builtins.bool'
13+
main:6: note: Revealed type is 'def (first: Any, second: Any, *, deps: Any =, backend: builtins.str =)'

0 commit comments

Comments
 (0)