-
Notifications
You must be signed in to change notification settings - Fork 2k
/
fixtures.py
253 lines (180 loc) · 6.62 KB
/
fixtures.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
# -*- coding: utf-8 -*-
"""This is a collection of pytest fixtures for use in tests.
All fixtures bellow available anywhere under the root of CKAN
repository. Any external CKAN extension should be able to include them
by adding next lines under root `conftest.py`
.. literalinclude:: /../conftest.py
There are three type of fixtures available in CKAN:
* Fixtures that have some side-effect. They don't return any useful
value and generally should be injected via
``pytest.mark.usefixtures``. Ex.: `with_plugins`, `clean_db`,
`clean_index`.
* Fixtures that provide value. Ex. `app`
* Fixtures that provide factory function. They are rarely needed, so
prefer using 'side-effect' or 'value' fixtures. Main use-case when
one may use function-fixture - late initialization or repeatable
execution(ex.: cleaning database more than once in a single
test). But presence of these fixtures in test usually signals that
is's a good time to refactor this test.
Deeper expanation can be found in `official documentation
<https://docs.pytest.org/en/latest/fixture.html>`_
"""
import functools
import smtplib
import pytest
import six
import mock
import rq
import ckan.tests.helpers as test_helpers
import ckan.plugins
import ckan.cli
import ckan.lib.search as search
from ckan.common import config
@pytest.fixture
def ckan_config(request, monkeypatch):
"""Allows to override the configuration object used by tests
Takes into account config patches introduced by the ``ckan_config``
mark.
If you just want to set one or more configuration options for the
scope of a test (or a test class), use the ``ckan_config`` mark::
@pytest.mark.ckan_config('ckan.auth.create_unowned_dataset', True)
def test_auth_create_unowned_dataset():
# ...
To use the custom config inside a test, apply the
``ckan_config`` mark to it and inject the ``ckan_config`` fixture:
.. literalinclude:: /../ckan/tests/pytest_ckan/test_fixtures.py
:start-after: # START-CONFIG-OVERRIDE
:end-before: # END-CONFIG-OVERRIDE
If the change only needs to be applied locally, use the
``monkeypatch`` fixture
.. literalinclude:: /../ckan/tests/test_common.py
:start-after: # START-CONFIG-OVERRIDE
:end-before: # END-CONFIG-OVERRIDE
"""
_original = config.copy()
for mark in request.node.iter_markers(u"ckan_config"):
monkeypatch.setitem(config, *mark.args)
yield config
config.clear()
config.update(_original)
@pytest.fixture
def make_app(ckan_config):
"""Factory for client app instances.
Unless you need to create app instances lazily for some reason,
use the ``app`` fixture instead.
"""
return test_helpers._get_test_app
@pytest.fixture
def app(make_app):
"""Returns a client app instance to use in functional tests
To use it, just add the ``app`` parameter to your test function signature::
def test_dataset_search(self, app):
url = h.url_for('dataset.search')
response = app.get(url)
"""
return make_app()
@pytest.fixture
def cli(ckan_config):
"""Provides object for invoking CLI commands from tests.
This is subclass of `click.testing.CliRunner`, so all examples
from `Click docs
<https://click.palletsprojects.com/en/master/testing/>`_ are valid
for it.
"""
env = {
u'CKAN_INI': ckan_config[u'__file__']
}
return test_helpers.CKANCliRunner(env=env)
@pytest.fixture(scope=u"session")
def reset_db():
"""Callable for resetting the database to the initial state.
If possible use the ``clean_db`` fixture instead.
"""
return test_helpers.reset_db
@pytest.fixture(scope=u"session")
def reset_index():
"""Callable for cleaning search index.
If possible use the ``clean_index`` fixture instead.
"""
return search.clear_all
@pytest.fixture
def clean_db(reset_db):
"""Resets the database to the initial state.
This can be used either for all tests in a class::
@pytest.mark.usefixtures("clean_db")
class TestExample(object):
def test_example(self):
or for a single test::
class TestExample(object):
@pytest.mark.usefixtures("clean_db")
def test_example(self):
"""
reset_db()
@pytest.fixture
def clean_index(reset_index):
"""Clear search index before starting the test.
"""
reset_index()
@pytest.fixture
def with_plugins(ckan_config):
"""Load all plugins specified by the ``ckan.plugins`` config option
at the beginning of the test. When the test ends (even it fails), it will
unload all the plugins in the reverse order.
.. literalinclude:: /../ckan/tests/test_factories.py
:start-after: # START-CONFIG-OVERRIDE
:end-before: # END-CONFIG-OVERRIDE
"""
plugins = ckan_config["ckan.plugins"].split()
for plugin in plugins:
if not ckan.plugins.plugin_loaded(plugin):
ckan.plugins.load(plugin)
yield
for plugin in reversed(plugins):
if ckan.plugins.plugin_loaded(plugin):
ckan.plugins.unload(plugin)
@pytest.fixture
def test_request_context(app):
"""Provide function for creating Flask request context.
"""
return app.flask_app.test_request_context
@pytest.fixture
def with_request_context(test_request_context):
"""Execute test inside requests context
"""
with test_request_context():
yield
@pytest.fixture
def mail_server(monkeypatch):
"""Catch all outcome mails.
"""
bag = test_helpers.FakeSMTP()
monkeypatch.setattr(smtplib, u"SMTP", bag)
yield bag
@pytest.fixture
def with_test_worker(monkeypatch):
"""Worker that doesn't create forks.
"""
if six.PY3:
monkeypatch.setattr(
rq.Worker, u"main_work_horse", rq.SimpleWorker.main_work_horse
)
monkeypatch.setattr(
rq.Worker, u"execute_job", rq.SimpleWorker.execute_job
)
yield
@pytest.fixture
def with_extended_cli(ckan_config, monkeypatch):
"""Enables effects of IClick.
Without this fixture, only CLI command that came from plugins
specified in real config file are available. When this fixture
enabled, changing `ckan.plugins` on test level allows to update
list of available CLI command.
"""
class MockConfig(object):
global_conf = ckan_config
local_conf = ckan_config
# Main `ckan` command is initialized from config file instead of
# using global config object. With this patch it becomes possible
# to apply per-test config changes to it without creating real
# config file.
monkeypatch.setattr(ckan.cli, u'load_config', lambda _: MockConfig())