Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions test-requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ pytest-localserver==0.5.0
pytest-cov==2.8.1
jsonschema==3.2.0
pyrsistent==0.16.0 # TODO(py3): 0.17.0 requires python3, see https://github.com/tobgu/pyrsistent/issues/205
mock # for testing under python < 3.3
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of doing this please just use the pytest monkeypatch fixture

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure how you mean. I do see how you could use monkeypatch for mock.patch.object calls, but how would you handle getting mock.Mock instances when you need them?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know how you're going to use mock but I've just been creating my own stub classes. I find a lot of mock's behavior too magical wrt attribute access on the mock obj


gevent
eventlet
Expand Down
86 changes: 86 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import os
import json
from types import FunctionType

import pytest
import jsonschema
Expand Down Expand Up @@ -36,6 +37,11 @@ def benchmark():
else:
del pytest_benchmark

try:
from unittest import mock # python 3.3 and above
except ImportError:
import mock # python < 3.3


@pytest.fixture(autouse=True)
def internal_exceptions(request, monkeypatch):
Expand Down Expand Up @@ -327,3 +333,83 @@ def render_span(span):
return "\n".join(render_span(root_span))

return inner


@pytest.fixture(name="StringContaining")
def string_containing_matcher():
"""
An object which matches any string containing the substring passed to the
object at instantiation time.

Useful for assert_called_with, assert_any_call, etc.

Used like this:

>>> f = mock.Mock(return_value=None)
>>> f("dogs are great")
>>> f.assert_any_call("dogs") # will raise AssertionError
Traceback (most recent call last):
...
AssertionError: mock('dogs') call not found
>>> f.assert_any_call(StringContaining("dogs")) # no AssertionError

"""

class StringContaining(object):
def __init__(self, substring):
self.substring = substring

def __eq__(self, test_string):
if not isinstance(test_string, str):
return False

return self.substring in test_string

return StringContaining


@pytest.fixture(name="DictionaryContaining")
def dictionary_containing_matcher():
"""
An object which matches any dictionary containing all key-value pairs from
the dictionary passed to the object at instantiation time.

Useful for assert_called_with, assert_any_call, etc.

Used like this:

>>> f = mock.Mock(return_value=None)
>>> f({"dogs": "yes", "cats": "maybe"})
>>> f.assert_any_call({"dogs": "yes"}) # will raise AssertionError
Traceback (most recent call last):
...
AssertionError: mock({'dogs': 'yes'}) call not found
>>> f.assert_any_call(DictionaryContaining({"dogs": "yes"})) # no AssertionError
"""

class DictionaryContaining(object):
def __init__(self, subdict):
self.subdict = subdict

def __eq__(self, test_dict):
if not isinstance(test_dict, dict):
return False

return all(test_dict.get(key) == self.subdict[key] for key in self.subdict)

return DictionaryContaining


@pytest.fixture(name="FunctionMock")
def function_mock():
"""
Just like a mock.Mock object, but one which always passes an isfunction
test.
"""

class FunctionMock(mock.Mock):
def __init__(self, *args, **kwargs):
super(FunctionMock, self).__init__(*args, **kwargs)
self.__class__ = FunctionType

return FunctionMock
Comment on lines +403 to +415
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know how you're going to use mock but I've just been creating my own stub classes. I find a lot of mock's behavior too magical wrt attribute access on the mock obj

Here, for instance:

def test_passes_custom_samling_context_from_start_transaction_to_traces_sampler(
    FunctionMock, sentry_init, DictionaryContaining
):
    traces_sampler = FunctionMock()
    sentry_init(traces_sampler=traces_sampler)

    start_transaction(custom_sampling_context={"dogs": "yes", "cats": "maybe"})

    traces_sampler.assert_any_call(
        DictionaryContaining({"dogs": "yes", "cats": "maybe"})
    )

Would rather not have to rewrite the machinery of tracking calls.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

def test_passes_custom_samling_context_from_start_transaction_to_traces_sampler(
    sentry_init
):
    sampler_calls = []
    sentry_init(traces_sampler=sampler_calls.append)

    start_transaction(custom_sampling_context={"dogs": "yes", "cats": "maybe"})

    assert sampler_calls == [{"dogs": "yes", "cats": "maybe"}]

Unless you need to account for additional keys or calls I would go with this...

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Feel free to add... I am not entirely sure why people keep using this stuff but I don't want to fight it.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, in the interest of time I'm going to leave it for now, but that's an interesting pattern to know about, thanks!

3 changes: 3 additions & 0 deletions tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,9 @@ envlist =

[testenv]
deps =
# if you change test-requirements.txt and your change is not being reflected
# in what's installed by tox (when running tox locally), try running tox
# with the -r flag
-r test-requirements.txt

django-{1.11,2.0,2.1,2.2,3.0,3.1,dev}: djangorestframework>=3.0.0,<4.0.0
Expand Down