-
Notifications
You must be signed in to change notification settings - Fork 54
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
11 changed files
with
356 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
include testslide-version | ||
include requirements.txt |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
[TestSlide](https://testslide.readthedocs.io/) fixture for pytest. | ||
|
||
## Quickstart | ||
|
||
Install: | ||
|
||
``` | ||
pip install pytest-testslide | ||
``` | ||
|
||
In your test file: | ||
``` | ||
import pytest | ||
from pytest_testslide import testslide | ||
from testslide import StrictMock # if you wish to use StrictMock | ||
from testslide import matchers # if you wish to use Rspec style argument matchers | ||
..... | ||
def test_mock_callable_patching_works(testslide): | ||
testslide.mock_callable(time, "sleep").to_raise(RuntimeError("Mocked!")) #mock_callable | ||
with pytest.raises(RuntimeError): | ||
time.sleep() | ||
@pytest.mark.asyncio | ||
async def test_mock_async_callable_patching_works(testslide): | ||
testslide.mock_async_callable(sample_module.ParentTarget, "async_static_method").to_raise(RuntimeError("Mocked!")) #mock_async_callable | ||
with pytest.raises(RuntimeError): | ||
await sample_module.ParentTarget.async_static_method("a", "b") | ||
def test_mock_constructor_patching_works(testslide): | ||
testslide.mock_constructor(sample_module, "ParentTarget").to_raise(RuntimeError("Mocked!")) #mock_constructor | ||
with pytest.raises(RuntimeError): | ||
sample_module.ParentTarget() | ||
def test_patch_attribute_patching_works(testslide): | ||
testslide.patch_attribute(sample_module.SomeClass, "attribute", "patched") #patch_attribute | ||
assert sample_module.SomeClass.attribute == "patched" | ||
``` | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
# Copyright (c) Facebook, Inc. and its affiliates. | ||
# | ||
# This source code is licensed under the MIT license found in the | ||
# LICENSE file in the root directory of this source tree. | ||
|
||
from types import TracebackType | ||
from typing import Any, Callable, Iterator, List, Optional | ||
|
||
import pytest | ||
|
||
import testslide as testslide_module | ||
|
||
|
||
class _TestSlideFixture: | ||
def _register_assertion(self, assertion: Callable) -> None: | ||
self._assertions.append(assertion) | ||
|
||
def __enter__(self) -> "_TestSlideFixture": | ||
self._assertions: List[Callable] = [] | ||
testslide_module.mock_callable.register_assertion = self._register_assertion | ||
return self | ||
|
||
def __exit__( | ||
self, | ||
exc_type: Optional[type], | ||
exc_val: Optional[Exception], | ||
exc_tb: TracebackType, | ||
): | ||
aggregated_exceptions = testslide_module.AggregatedExceptions() | ||
try: | ||
for assertion in self._assertions: | ||
try: | ||
assertion() | ||
except BaseException as be: | ||
aggregated_exceptions.append_exception(be) | ||
|
||
finally: | ||
testslide_module.mock_callable.unpatch_all_callable_mocks() | ||
testslide_module.mock_constructor.unpatch_all_constructor_mocks() | ||
testslide_module.patch_attribute.unpatch_all_mocked_attributes() | ||
if aggregated_exceptions.exceptions: | ||
pytest.fail(str(aggregated_exceptions), False) | ||
|
||
@staticmethod | ||
def mock_callable( | ||
*args: Any, **kwargs: Any | ||
) -> testslide_module.mock_callable._MockCallableDSL: | ||
return testslide_module.mock_callable.mock_callable(*args, **kwargs) | ||
|
||
@staticmethod | ||
def mock_async_callable( | ||
*args: Any, **kwargs: Any | ||
) -> testslide_module.mock_callable._MockAsyncCallableDSL: | ||
return testslide_module.mock_callable.mock_async_callable(*args, **kwargs) | ||
|
||
@staticmethod | ||
def mock_constructor( | ||
*args: Any, **kwargs: Any | ||
) -> testslide_module.mock_constructor._MockConstructorDSL: | ||
return testslide_module.mock_constructor.mock_constructor(*args, **kwargs) | ||
|
||
@staticmethod | ||
def patch_attribute(*args: Any, **kwargs: Any) -> None: | ||
return testslide_module.patch_attribute.patch_attribute(*args, **kwargs) | ||
|
||
|
||
@pytest.fixture | ||
def testslide() -> Iterator[_TestSlideFixture]: | ||
with _TestSlideFixture() as testslide_fixture: | ||
yield testslide_fixture |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
pytest~=6.2 | ||
pytest-asyncio>=0.14.0 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
# Copyright (c) Facebook, Inc. and its affiliates. | ||
# | ||
# This source code is licensed under the MIT license found in the | ||
# LICENSE file in the root directory of this source tree. | ||
|
||
import os | ||
|
||
from setuptools import setup | ||
|
||
dir_path = os.path.dirname(os.path.realpath(__file__)) | ||
|
||
ts_version = ( | ||
open(os.path.join(dir_path, os.pardir, "testslide_version")).read().rstrip() | ||
) | ||
requirements = open("requirements.txt", encoding="utf8").readlines() | ||
|
||
with open("README.md", encoding="utf8") as f: | ||
readme = f.read() | ||
|
||
setup( | ||
name="pytest-testslide", | ||
version=ts_version, | ||
py_modules=["pytest_testslide"], | ||
maintainer="Balint Csergo", | ||
maintainer_email="deathowlzz@gmail.com", | ||
url="https://github.com/facebookincubator/TestSlide/tree/master/pytest-testslide", | ||
license="MIT", | ||
description="TestSlide fixture for pytest", | ||
long_description=readme, | ||
long_description_content_type="text/markdown", | ||
setup_requires=["setuptools>=38.6.0"], | ||
install_requires=[ | ||
f"testslide>={ts_version}", | ||
] | ||
+ requirements, | ||
extras_require={"build": ["black", "flake8", "mypy"]}, | ||
classifiers=[ | ||
"Development Status :: 5 - Production/Stable", | ||
"Intended Audience :: Developers", | ||
"License :: OSI Approved :: MIT License", | ||
"Operating System :: POSIX :: Linux", | ||
"Programming Language :: Python :: 3", | ||
"Topic :: Software Development :: Testing", | ||
"Topic :: Software Development :: Testing :: Acceptance", | ||
"Topic :: Software Development :: Testing :: BDD", | ||
"Topic :: Software Development :: Testing :: Mocking", | ||
"Topic :: Software Development :: Testing :: Unit ", | ||
], | ||
entry_points={"pytest11": ["pytest-testslide = pytest_testslide"]}, | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
# Copyright (c) Facebook, Inc. and its affiliates. | ||
# | ||
# This source code is licensed under the MIT license found in the | ||
# LICENSE file in the root directory of this source tree. | ||
|
||
pytest_plugins = "pytester" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,168 @@ | ||
# Copyright (c) Facebook, Inc. and its affiliates. | ||
# | ||
# This source code is licensed under the MIT license found in the | ||
# LICENSE file in the root directory of this source tree. | ||
import re | ||
|
||
|
||
def test_pass(testdir): | ||
testdir.makepyfile( | ||
""" | ||
import time | ||
import pytest | ||
from pytest_testslide import testslide | ||
from tests import sample_module | ||
from testslide import StrictMock | ||
def test_has_mock_callable(testslide): | ||
testslide.mock_callable | ||
def test_mock_callable_assertion_works(testslide): | ||
testslide.mock_callable | ||
def test_mock_callable_unpaches(testslide): | ||
testslide.mock_callable | ||
def test_has_mock_async_callable(testslide): | ||
testslide.mock_async_callable | ||
def test_mock_async_callable_assertion_works(testslide): | ||
testslide.mock_async_callable | ||
def test_mock_async_callable_unpaches(testslide): | ||
testslide.mock_async_callable | ||
def test_has_mock_constructor(testslide): | ||
testslide.mock_constructor | ||
def test_mock_constructor_assertion_works(testslide): | ||
testslide.mock_constructor | ||
def test_mock_constructor_unpaches(testslide): | ||
testslide.mock_constructor | ||
def test_has_patch_attribute(testslide): | ||
testslide.patch_attribute | ||
def test_patch_attribute_unpaches(testslide): | ||
testslide.patch_attribute | ||
# mock_callable integration tests | ||
def test_mock_callable_patching_works(testslide): | ||
testslide.mock_callable(time, "sleep").to_raise(RuntimeError("Mocked!")) | ||
with pytest.raises(RuntimeError): | ||
time.sleep() | ||
def test_mock_callable_unpatching_works(testslide): | ||
# This will fail if unpatching from test_mock_callable_patching_works does | ||
# not happen | ||
time.sleep(0) | ||
def test_mock_callable_assertion_works(testslide): | ||
testslide.mock_callable("time", "sleep").for_call(0).to_call_original().and_assert_called_once() | ||
time.sleep(0) | ||
def test_mock_callable_failing_assertion_works(testslide): | ||
testslide.mock_callable("time", "sleep").for_call(0).to_call_original().and_assert_called_once() | ||
# mock_async_callable integration test | ||
@pytest.mark.asyncio | ||
async def test_mock_async_callable_patching_works(testslide): | ||
testslide.mock_async_callable(sample_module.ParentTarget, "async_static_method").to_raise(RuntimeError("Mocked!")) | ||
with pytest.raises(RuntimeError): | ||
await sample_module.ParentTarget.async_static_method("a", "b") | ||
@pytest.mark.asyncio | ||
async def test_mock_async_callable_unpatching_works(testslide): | ||
# This will fail if unpatching from test_mock_async_callable_patching_works does | ||
# not happen | ||
assert await sample_module.ParentTarget.async_static_method("a", "b") == "async original response" | ||
@pytest.mark.asyncio | ||
async def test_mock_async_callable_assertion_works(testslide): | ||
testslide.mock_async_callable(sample_module.ParentTarget, "async_static_method").for_call("a", "b").to_call_original().and_assert_called_once() | ||
await sample_module.ParentTarget.async_static_method("a", "b") | ||
def test_mock_async_callable_failing_assertion_works(testslide): | ||
testslide.mock_async_callable(sample_module.ParentTarget, "async_static_method").for_call("a", "b").to_call_original().and_assert_called_once() | ||
# mock_constructor integration test | ||
def test_mock_constructor_patching_works(testslide): | ||
testslide.mock_constructor(sample_module, "ParentTarget").to_raise(RuntimeError("Mocked!")) | ||
with pytest.raises(RuntimeError): | ||
sample_module.ParentTarget() | ||
def test_mock_constructor_unpatching_works(testslide): | ||
# This will fail if unpatching from test_mock_constructor_patching_works does | ||
# not happen | ||
assert sample_module.ParentTarget() | ||
def test_mock_constructor_assertion_works(testslide): | ||
testslide.mock_constructor(sample_module, "ParentTarget").to_call_original().and_assert_called_once() | ||
sample_module.ParentTarget() | ||
def test_mock_constructor_failing_assertion_works(testslide): | ||
testslide.mock_constructor(sample_module, "ParentTarget").to_call_original().and_assert_called_once() | ||
# patch_attribute integration test | ||
def test_patch_attribute_patching_works(testslide): | ||
testslide.patch_attribute(sample_module.SomeClass, "attribute", "patched") | ||
assert sample_module.SomeClass.attribute == "patched" | ||
def test_patch_attribute_unpatching_works(testslide): | ||
# This will fail if unpatching from test_mock_callable_patching_works does | ||
# not happen | ||
assert sample_module.SomeClass.attribute == "value" | ||
def test_aggregated_exceptions(testslide): | ||
mocked_cls = StrictMock(sample_module.CallOrderTarget) | ||
testslide.mock_callable(mocked_cls, 'f1')\ | ||
.for_call("a").to_return_value("mocked")\ | ||
.and_assert_called_once() | ||
testslide.mock_callable(mocked_cls, 'f1')\ | ||
.for_call("b").to_return_value("mocked2")\ | ||
.and_assert_called_once() | ||
sample_module.CallOrderTarget("c").f1("a") | ||
""" | ||
) | ||
result = testdir.runpytest("-v") | ||
assert "passed, 4 errors" in result.stdout.str() | ||
assert "failed" not in result.stdout.str() | ||
expected_failure = re.compile( | ||
""".*_______ ERROR at teardown of test_mock_callable_failing_assertion_works ________ | ||
1 failures. | ||
<class \'AssertionError\'>: calls did not match assertion. | ||
\'time\', \'sleep\': | ||
expected: called exactly 1 time\(s\) with arguments: | ||
\(0,\) | ||
received: 0 call\(s\) | ||
____ ERROR at teardown of test_mock_async_callable_failing_assertion_works _____ | ||
1 failures. | ||
<class \'AssertionError\'>: calls did not match assertion. | ||
<class \'tests.sample_module.ParentTarget\'>, \'async_static_method\': | ||
expected: called exactly 1 time\(s\) with arguments: | ||
\(\'a\', \'b\'\) | ||
received: 0 call\(s\) | ||
______ ERROR at teardown of test_mock_constructor_failing_assertion_works ______ | ||
1 failures. | ||
<class \'AssertionError\'>: calls did not match assertion. | ||
<class \'testslide.mock_constructor.ParentTarget\'>, \'__new__\': | ||
expected: called exactly 1 time\(s\) with any arguments received: 0 call\(s\) | ||
_______________ ERROR at teardown of test_aggregated_exceptions ________________ | ||
2 failures. | ||
<class \'AssertionError\'>: calls did not match assertion. | ||
<StrictMock 0x[a-fA-F0-9]+ template=tests.sample_module.CallOrderTarget .*/test_pass0/test_pass.py:[0-9]+>, \'f1\': | ||
expected: called exactly 1 time\(s\) with arguments: | ||
\(\'a\',\) | ||
received: 0 call\(s\) | ||
<class \'AssertionError\'>: calls did not match assertion. | ||
<StrictMock 0x[a-fA-F0-9]+ template=tests.sample_module.CallOrderTarget .*/test_pass0/test_pass.py:[0-9]+>, \'f1\': | ||
expected: called exactly 1 time\(s\) with arguments: | ||
\(\'b\',\) | ||
received: 0 call\(s\).*""", | ||
re.MULTILINE | re.DOTALL, | ||
) | ||
assert expected_failure.match(result.stdout.str()) | ||
assert result.ret != 0 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
2.6.4 |
Oops, something went wrong.