zope.pytestlayer 8.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. zope/pytestlayer/__init__.py +0 -0
  2. zope/pytestlayer/_compat.py +18 -0
  3. zope/pytestlayer/doctest.py +31 -0
  4. zope/pytestlayer/fixture.py +234 -0
  5. zope/pytestlayer/layered.py +100 -0
  6. zope/pytestlayer/plugin.py +122 -0
  7. zope/pytestlayer/testing.py +6 -0
  8. zope/pytestlayer/tests/__init__.py +0 -0
  9. zope/pytestlayer/tests/conftest.py +3 -0
  10. zope/pytestlayer/tests/fixture/bad_layer/__init__.py +0 -0
  11. zope/pytestlayer/tests/fixture/bad_layer/conftest.py +1 -0
  12. zope/pytestlayer/tests/fixture/bad_layer/test_core.py +13 -0
  13. zope/pytestlayer/tests/fixture/custom_fixture_name/__init__.py +0 -0
  14. zope/pytestlayer/tests/fixture/custom_fixture_name/conftest.py +12 -0
  15. zope/pytestlayer/tests/fixture/custom_fixture_name/test_core.py +38 -0
  16. zope/pytestlayer/tests/fixture/keep_layer_across_test_classes/__init__.py +0 -0
  17. zope/pytestlayer/tests/fixture/keep_layer_across_test_classes/conftest.py +1 -0
  18. zope/pytestlayer/tests/fixture/keep_layer_across_test_classes/test_core.py +101 -0
  19. zope/pytestlayer/tests/fixture/layers_with_same_name/__init__.py +0 -0
  20. zope/pytestlayer/tests/fixture/layers_with_same_name/conftest.py +1 -0
  21. zope/pytestlayer/tests/fixture/layers_with_same_name/test_core.py +40 -0
  22. zope/pytestlayer/tests/fixture/no_setup_or_teardown/__init__.py +0 -0
  23. zope/pytestlayer/tests/fixture/no_setup_or_teardown/conftest.py +1 -0
  24. zope/pytestlayer/tests/fixture/no_setup_or_teardown/test_core.py +14 -0
  25. zope/pytestlayer/tests/fixture/order_by_layer/__init__.py +0 -0
  26. zope/pytestlayer/tests/fixture/order_by_layer/conftest.py +1 -0
  27. zope/pytestlayer/tests/fixture/order_by_layer/test_core.py +112 -0
  28. zope/pytestlayer/tests/fixture/order_with_layered_suite/__init__.py +0 -0
  29. zope/pytestlayer/tests/fixture/order_with_layered_suite/conftest.py +1 -0
  30. zope/pytestlayer/tests/fixture/order_with_layered_suite/foo.txt +14 -0
  31. zope/pytestlayer/tests/fixture/order_with_layered_suite/foobar.txt +14 -0
  32. zope/pytestlayer/tests/fixture/order_with_layered_suite/test_core.py +139 -0
  33. zope/pytestlayer/tests/fixture/session_fixture/__init__.py +0 -0
  34. zope/pytestlayer/tests/fixture/session_fixture/conftest.py +13 -0
  35. zope/pytestlayer/tests/fixture/session_fixture/test_core.py +37 -0
  36. zope/pytestlayer/tests/fixture/shared_with_layered_suite/__init__.py +0 -0
  37. zope/pytestlayer/tests/fixture/shared_with_layered_suite/conftest.py +1 -0
  38. zope/pytestlayer/tests/fixture/shared_with_layered_suite/mydoctest.txt +10 -0
  39. zope/pytestlayer/tests/fixture/shared_with_layered_suite/test_core.py +53 -0
  40. zope/pytestlayer/tests/fixture/single_layer/__init__.py +0 -0
  41. zope/pytestlayer/tests/fixture/single_layer/conftest.py +1 -0
  42. zope/pytestlayer/tests/fixture/single_layer/test_core.py +33 -0
  43. zope/pytestlayer/tests/fixture/single_layer_in_two_modules/__init__.py +0 -0
  44. zope/pytestlayer/tests/fixture/single_layer_in_two_modules/conftest.py +1 -0
  45. zope/pytestlayer/tests/fixture/single_layer_in_two_modules/test_core.py +33 -0
  46. zope/pytestlayer/tests/fixture/single_layer_in_two_modules/test_second_module.py +12 -0
  47. zope/pytestlayer/tests/fixture/single_layer_with_unattached_base_layer/__init__.py +0 -0
  48. zope/pytestlayer/tests/fixture/single_layer_with_unattached_base_layer/conftest.py +1 -0
  49. zope/pytestlayer/tests/fixture/single_layer_with_unattached_base_layer/test_core.py +54 -0
  50. zope/pytestlayer/tests/fixture/single_layered_suite/__init__.py +0 -0
  51. zope/pytestlayer/tests/fixture/single_layered_suite/conftest.py +1 -0
  52. zope/pytestlayer/tests/fixture/single_layered_suite/doctest.txt +10 -0
  53. zope/pytestlayer/tests/fixture/single_layered_suite/test_core.py +44 -0
  54. zope/pytestlayer/tests/fixture/two_dependent_layered_suites/__init__.py +0 -0
  55. zope/pytestlayer/tests/fixture/two_dependent_layered_suites/bar.txt +14 -0
  56. zope/pytestlayer/tests/fixture/two_dependent_layered_suites/conftest.py +1 -0
  57. zope/pytestlayer/tests/fixture/two_dependent_layered_suites/foo.txt +14 -0
  58. zope/pytestlayer/tests/fixture/two_dependent_layered_suites/test_core.py +72 -0
  59. zope/pytestlayer/tests/fixture/two_dependent_layers/__init__.py +0 -0
  60. zope/pytestlayer/tests/fixture/two_dependent_layers/conftest.py +1 -0
  61. zope/pytestlayer/tests/fixture/two_dependent_layers/test_core.py +67 -0
  62. zope/pytestlayer/tests/fixture/two_independent_layers/__init__.py +0 -0
  63. zope/pytestlayer/tests/fixture/two_independent_layers/conftest.py +1 -0
  64. zope/pytestlayer/tests/fixture/two_independent_layers/test_core.py +67 -0
  65. zope/pytestlayer/tests/fixture/with_and_without_layer/__init__.py +0 -0
  66. zope/pytestlayer/tests/fixture/with_and_without_layer/conftest.py +1 -0
  67. zope/pytestlayer/tests/fixture/with_and_without_layer/test_core.py +39 -0
  68. zope/pytestlayer/tests/test_doctest.py +16 -0
  69. zope/pytestlayer/tests/test_fixture.py +17 -0
  70. zope/pytestlayer/tests/test_integration.py +524 -0
  71. zope/pytestlayer/tests/test_layer.py +13 -0
  72. zope.pytestlayer-8.2-py3.11-nspkg.pth +1 -0
  73. zope.pytestlayer-8.2.dist-info/LICENSE.txt +43 -0
  74. zope.pytestlayer-8.2.dist-info/METADATA +318 -0
  75. zope.pytestlayer-8.2.dist-info/RECORD +79 -0
  76. zope.pytestlayer-8.2.dist-info/WHEEL +5 -0
  77. zope.pytestlayer-8.2.dist-info/entry_points.txt +2 -0
  78. zope.pytestlayer-8.2.dist-info/namespace_packages.txt +1 -0
  79. zope.pytestlayer-8.2.dist-info/top_level.txt +1 -0
File without changes
@@ -0,0 +1,18 @@
1
+ # copied from Python 2.7 source code as plone testing uses __bases__
2
+ def _searchbases(cls, accum):
3
+ # Simulate the "classic class" search order.
4
+ if cls in accum:
5
+ return
6
+ accum.append(cls)
7
+ for base in cls.__bases__:
8
+ _searchbases(base, accum)
9
+
10
+
11
+ def getmro(cls):
12
+ """Return tuple of base classes in method resolution order."""
13
+ if hasattr(cls, "__mro__"):
14
+ return cls.__mro__
15
+ else:
16
+ result = []
17
+ _searchbases(cls, result)
18
+ return tuple(result)
@@ -0,0 +1,31 @@
1
+ import doctest
2
+
3
+
4
+ class NoOpLayer:
5
+ """Layer needed for zope.pytestlayer to find and run doctests.
6
+
7
+ See https://github.com/zope/zope.pytestlayer/issues/4
8
+ """
9
+
10
+ __name__ = 'NoOpLayer'
11
+ __bases__ = ()
12
+
13
+ def setUp(self):
14
+ pass
15
+
16
+ def tearDown(self):
17
+ pass
18
+
19
+
20
+ NOOP_LAYER = NoOpLayer()
21
+
22
+
23
+ def DocTestSuite(*args, **kw):
24
+ """A DocTestSuite whose tests are detectable by zope.pytestlayer.
25
+
26
+ See https://github.com/zope/zope.pytestlayer/issues/4
27
+ """
28
+ layer = kw.pop('layer', NOOP_LAYER)
29
+ suite = doctest.DocTestSuite(*args, **kw)
30
+ suite.layer = layer
31
+ return suite
@@ -0,0 +1,234 @@
1
+ import contextlib
2
+ import re
3
+ import time
4
+ import types
5
+
6
+ import pytest
7
+ import zope.dottedname.resolve
8
+
9
+
10
+ class ZopeLayerState(object):
11
+
12
+ def __init__(self):
13
+ self.current = set()
14
+ self.keep = set()
15
+ self.keep_for_whole_session = set()
16
+
17
+
18
+ @contextlib.contextmanager
19
+ def timer(request, text):
20
+ verbose = request.config.option.verbose > 0
21
+ reporter = request.config.pluginmanager.getplugin('terminalreporter')
22
+ if verbose:
23
+ reporter.ensure_newline()
24
+ reporter.write(text)
25
+ start = time.time()
26
+ yield
27
+ if verbose:
28
+ time_taken = time.time() - start
29
+ reporter.write(f"{time_taken:.3f}", green=1, bold=1)
30
+ reporter.write_line(" seconds.")
31
+
32
+
33
+ def setup_layer(layer, request):
34
+ decorate_layer(layer, request)
35
+ state = request.session.zopelayer_state
36
+ layer_name = get_layer_name(layer)
37
+ if hasattr(layer, 'setUp'):
38
+ print(layer_name)
39
+ with timer(request, f"Set up {layer_name} in "):
40
+ layer.setUp()
41
+ state.current.add(layer)
42
+
43
+
44
+ def teardown_layer(layer, request):
45
+ decorate_layer(layer, request)
46
+ state = request.session.zopelayer_state
47
+ layer_name = get_layer_name(layer)
48
+ if hasattr(layer, 'tearDown'):
49
+ with timer(request, f"Tear down {layer_name} in "):
50
+ layer.tearDown()
51
+ state.current.remove(layer)
52
+
53
+
54
+ def session_fixture(request, layer):
55
+ state = request.session.zopelayer_state
56
+ if layer not in state.current:
57
+ setup_layer(layer, request)
58
+ state.keep_for_whole_session.add(layer)
59
+
60
+ def teardown():
61
+ teardown_layer(layer, request)
62
+ request.addfinalizer(teardown)
63
+ return layer
64
+
65
+
66
+ def class_fixture(request, layer):
67
+ state = request.session.zopelayer_state
68
+ if layer not in state.current:
69
+ setup_layer(layer, request)
70
+
71
+ def maybe_teardown():
72
+ if layer not in (state.keep | state.keep_for_whole_session):
73
+ teardown_layer(layer, request)
74
+ request.addfinalizer(maybe_teardown)
75
+ return layer
76
+
77
+
78
+ def function_fixture(request, layer):
79
+ decorate_layer(layer, request)
80
+ if hasattr(layer, 'testSetUp'):
81
+ layer.testSetUp()
82
+
83
+ if hasattr(layer, 'testTearDown'):
84
+
85
+ def function_tear_down():
86
+ decorate_layer(layer, request)
87
+ layer.testTearDown()
88
+
89
+ request.addfinalizer(function_tear_down)
90
+ return layer
91
+
92
+
93
+ def decorate_layer(layer, request):
94
+ setattr(layer, 'pytest_request', request)
95
+
96
+
97
+ def get_layer_name(layer):
98
+ module = zope.dottedname.resolve.resolve(layer.__module__)
99
+ for key, value in module.__dict__.items():
100
+ if value is layer:
101
+ name = key
102
+ break
103
+ else:
104
+ # As per zope.testrunner conventions, a layer is assumed to have a
105
+ # __name__ even if it's not a class.
106
+ name = layer.__name__
107
+ return '%s.%s' % (layer.__module__, name)
108
+
109
+
110
+ def make_identifier(string):
111
+ # Replaces things between words into underscores:
112
+ return re.sub(r'\W|^(?=\d)', '_', string)
113
+
114
+
115
+ def get_fixture_name(layer, scope):
116
+ name = make_identifier(get_layer_name(layer))
117
+ layerid = id(layer)
118
+ return 'zope_layer_{scope}_{name}_{layerid}'.format(**locals())
119
+
120
+
121
+ LAYERS = {}
122
+ LAYERS[object] = {} # We do not need to create a fixture for `object`
123
+
124
+
125
+ def create(*layers, **kw):
126
+ """Create fixtures for given layers and their bases.
127
+
128
+ Fixture names will be generated automatically. For a single layer, you can
129
+ pass in kw arguments ``session_fixture_name, ``class_fixture_name`` and
130
+ ``function_fixture_name`` instead.
131
+
132
+ """
133
+ if kw and len(layers) > 1:
134
+ raise ValueError(
135
+ 'Overriding layer names is only possible '
136
+ 'for a single layer at a time')
137
+
138
+ ns = {}
139
+ for layer in layers:
140
+ if isinstance(layer, str):
141
+ layer = zope.dottedname.resolve.resolve(layer)
142
+ ns.update(_create_single(layer, **kw))
143
+ return ns
144
+
145
+
146
+ SCOPES = ('session', 'class', 'function')
147
+ TEMPLATE = """\
148
+ @pytest.fixture(scope='session')
149
+ def {session_fixture_name}(request{session_fixture_dependencies}):
150
+ "Depends on {session_fixture_dependencies}"
151
+ return session_fixture(request, layer)
152
+
153
+ @pytest.fixture(scope='class')
154
+ def {class_fixture_name}(request{class_fixture_dependencies}):
155
+ "Depends on {class_fixture_dependencies}"
156
+ return class_fixture(request, layer)
157
+
158
+ @pytest.fixture(scope='function')
159
+ def {function_fixture_name}(request{function_fixture_dependencies}):
160
+ "Depends on {function_fixture_dependencies}"
161
+ return function_fixture(request, layer)
162
+ """
163
+
164
+
165
+ def _create_single(layer, **kw):
166
+ """Actually create a fixtures for a single layer and its bases."""
167
+ if layer in LAYERS:
168
+ return {}
169
+
170
+ LAYERS[layer] = {}
171
+ dependencies = {}
172
+ for scope in SCOPES:
173
+ LAYERS[layer][scope] = kw.get(
174
+ '%s_fixture_name' % scope, get_fixture_name(layer, scope))
175
+ dependencies[scope] = [
176
+ ', ' + LAYERS.get(base, {}).get(
177
+ scope, get_fixture_name(base, scope))
178
+ for base in layer.__bases__ if base is not object]
179
+ dependencies['function'].insert(0, ', ' + LAYERS[layer]['class'])
180
+
181
+ fixtures = {}
182
+ for scope in SCOPES:
183
+ fixtures['%s_fixture_name' % scope] = LAYERS[layer][scope]
184
+ fixtures['%s_fixture_dependencies' % scope] = ''.join(
185
+ dependencies[scope])
186
+ code = TEMPLATE.format(**fixtures)
187
+
188
+ globs = {
189
+ 'pytest': pytest,
190
+ 'layer': layer,
191
+ }
192
+ for scope in SCOPES:
193
+ globs['%s_fixture' % scope] = globals()['%s_fixture' % scope]
194
+
195
+ ns = {}
196
+ exec(code, globs, ns)
197
+
198
+ # Recurse into bases:
199
+ ns.update(create(*layer.__bases__))
200
+
201
+ return ns
202
+
203
+
204
+ def parsefactories(collector, layer):
205
+ ns = create(layer)
206
+ if ns:
207
+ name = get_fixture_name(layer, scope='function')
208
+ module = types.ModuleType(name)
209
+ module.__dict__.update(ns)
210
+ collector.session._fixturemanager.parsefactories(module, '')
211
+
212
+
213
+ def raise_if_bad_layer(layer):
214
+ 'complaining about bad layers'
215
+
216
+ if not hasattr(layer, '__bases__'):
217
+ raise RuntimeError(
218
+ "The layer {layer!r} has no __bases__ attribute."
219
+ " Layers may be of two sorts: class or instance with __bases__"
220
+ " attribute."
221
+ )
222
+
223
+
224
+ KEYWORDS_BY_LAYER = {object: {}}
225
+
226
+
227
+ def get_keywords(layer):
228
+ if layer in KEYWORDS_BY_LAYER:
229
+ return KEYWORDS_BY_LAYER[layer]
230
+ keywords = {get_layer_name(layer): True}
231
+ for base_layer in layer.__bases__:
232
+ keywords.update(get_keywords(base_layer))
233
+ KEYWORDS_BY_LAYER[layer] = keywords
234
+ return keywords
@@ -0,0 +1,100 @@
1
+ import unittest
2
+
3
+ import _pytest.unittest
4
+ import pytest
5
+
6
+ from zope.pytestlayer import fixture
7
+
8
+
9
+ class LayeredTestSuite(pytest.Class):
10
+
11
+ def collect(self):
12
+ suite = self.obj()
13
+ for item, layer in walk_suite(suite):
14
+ fixture.parsefactories(self.parent, layer)
15
+ yield LayeredTestCaseInstance.from_parent(
16
+ parent=self, obj=item, layer=layer)
17
+
18
+
19
+ class LayeredTestCaseInstance(_pytest.unittest.UnitTestCase):
20
+
21
+ @classmethod
22
+ def from_parent(cls, parent, obj, layer, **kw):
23
+ testname = repr(obj) # fantastic doctest API :(
24
+ instance = super(pytest.Collector, cls).from_parent(
25
+ parent=parent, name=testname)
26
+ # store testcase instance and layer
27
+ # to pass them to function
28
+ instance.obj = obj
29
+ instance.layer = layer
30
+ instance.extra_keyword_matches.update(fixture.get_keywords(layer))
31
+ return instance
32
+
33
+ def collect(self):
34
+ yield LayeredTestCaseFunction.from_parent(parent=self, name='runTest')
35
+
36
+ def reportinfo(self):
37
+ pass
38
+
39
+
40
+ class LayeredTestCaseFunction(_pytest.unittest.TestCaseFunction):
41
+
42
+ @classmethod
43
+ def from_parent(cls, parent, name, **kw):
44
+ description = get_description(parent)
45
+ keywords = get_keywords(description)
46
+ function = super(LayeredTestCaseFunction, cls).from_parent(
47
+ parent=parent,
48
+ name=name,
49
+ keywords=keywords,
50
+ )
51
+ function.layer = function.parent.layer
52
+ function.tc_description = description
53
+ function._testcase = function.parent.obj
54
+ return function
55
+
56
+ def setup(self):
57
+ # This is actually set in the base class, but as we want to modify
58
+ # `self._request` in our way, we do not make a super call here.
59
+ # It has to be None or a bound method to be called during tearDown.
60
+ self._explicit_tearDown = None
61
+ if hasattr(self, "_request"):
62
+ # call function fixture (testSetUp)
63
+ fixture_name = fixture.get_fixture_name(
64
+ self.layer, scope='function')
65
+ self._request.getfixturevalue(fixture_name)
66
+
67
+ def teardown(self):
68
+ _testcase = self._testcase
69
+ super(LayeredTestCaseFunction, self).teardown()
70
+ # Do not die with a meaningless error message when rerunning doctests:
71
+ self._testcase = _testcase
72
+
73
+ def reportinfo(self):
74
+ return ('test_suite', None, self.tc_description)
75
+
76
+
77
+ def get_description(collector):
78
+ description = str(collector.obj)
79
+ fspath = collector.session.fspath.strpath
80
+ return description.replace(fspath, '')
81
+
82
+
83
+ def get_keywords(description):
84
+ words = [word for word in description.split()]
85
+ keywords = {}
86
+ for word in words:
87
+ keywords[word] = True
88
+ return keywords
89
+
90
+
91
+ def walk_suite(suite):
92
+ if isinstance(suite, unittest.TestSuite):
93
+ has_layer = hasattr(suite, 'layer')
94
+ for item in suite:
95
+ if isinstance(item, unittest.TestCase) and has_layer:
96
+ fixture.raise_if_bad_layer(suite.layer)
97
+ yield item, suite.layer
98
+ else:
99
+ for result in walk_suite(item):
100
+ yield result
@@ -0,0 +1,122 @@
1
+ import types
2
+ import unittest
3
+
4
+ import pytest
5
+
6
+ from zope.pytestlayer import fixture
7
+ from zope.pytestlayer import layered
8
+
9
+ from ._compat import getmro
10
+
11
+
12
+ @pytest.mark.tryfirst
13
+ def pytest_pycollect_makeitem(collector, name, obj):
14
+ # this works because of two things:
15
+ # * this plugin is called before the pytest unittest collector (if it
16
+ # wasn't, it wouldn't be called at all after the pytest collector has
17
+ # detected a unittest test case)
18
+ # * usefixtures works in-place
19
+
20
+ suite = query_testsuite(obj)
21
+ if suite is not None:
22
+ return layered.LayeredTestSuite.from_parent(
23
+ parent=collector, name=name)
24
+ else:
25
+ layer = query_layer(obj)
26
+ if layer is not None:
27
+ fixture.parsefactories(collector, layer)
28
+ return collect_with_layer(collector, name, obj, layer)
29
+
30
+
31
+ def query_testsuite(obj):
32
+ if (isinstance(obj, types.FunctionType) and obj.__name__ == 'test_suite'):
33
+ suite = obj()
34
+ if isinstance(suite, unittest.TestSuite):
35
+ return suite
36
+
37
+
38
+ def query_layer(obj):
39
+ if has_layer(obj):
40
+ layer = obj.layer
41
+ fixture.raise_if_bad_layer(layer)
42
+ return layer
43
+
44
+
45
+ def has_layer(obj):
46
+ try:
47
+ isunit = issubclass(obj, unittest.TestCase)
48
+ except TypeError:
49
+ isunit = False
50
+ return isunit and hasattr(obj, 'layer')
51
+
52
+
53
+ def pytest_collection_modifyitems(session, config, items):
54
+ items_by_layer = {}
55
+ layers_in_order = []
56
+ for item in items:
57
+ if hasattr(item, 'cls') and hasattr(item.cls, 'layer'):
58
+ layer = item.cls.layer
59
+ layers_in_order.append(layer)
60
+ elif hasattr(item, 'layer'):
61
+ layer = item.layer
62
+ layers_in_order.append(layer)
63
+ else:
64
+ layer = None
65
+ items_by_layer.setdefault(layer, []).append(item)
66
+ ordered_layers = order_by_bases(layers_in_order)
67
+ items[:] = items_by_layer.get(None, [])
68
+ for layer in ordered_layers:
69
+ items.extend(items_by_layer.get(layer, []))
70
+
71
+
72
+ def order_by_bases(layers):
73
+ """Order the layers from least to most specific (bottom to top)
74
+ """
75
+ gathered = []
76
+ for layer in layers:
77
+ gather_layers(layer, gathered)
78
+ seen = set()
79
+ result = []
80
+ for layer in gathered:
81
+ if layer not in seen:
82
+ seen.add(layer)
83
+ if layer in layers:
84
+ result.append(layer)
85
+ return result
86
+
87
+
88
+ def gather_layers(layer, result):
89
+ for b in layer.__bases__:
90
+ gather_layers(b, result)
91
+ if layer is not object:
92
+ result.append(layer)
93
+
94
+
95
+ def pytest_sessionstart(session):
96
+ session.zopelayer_state = fixture.ZopeLayerState()
97
+
98
+
99
+ def pytest_runtest_teardown(item, nextitem):
100
+ state = item.session.zopelayer_state
101
+
102
+ if hasattr(nextitem, 'cls') and hasattr(nextitem.cls, 'layer'):
103
+ state.keep = state.current & set(getmro(nextitem.cls.layer))
104
+ elif hasattr(nextitem, 'layer'):
105
+ state.keep = state.current & set(getmro(nextitem.layer))
106
+ else:
107
+ state.keep.clear()
108
+
109
+
110
+ def collect_with_layer(collector, name, obj, layer):
111
+ fixture_name = fixture.LAYERS.get(layer, {}).get(
112
+ 'function', fixture.get_fixture_name(layer, 'function'))
113
+ usefixtures = pytest.mark.usefixtures(fixture_name)
114
+ usefixtures(obj)
115
+ py_unittest = get_py_unittest(collector)
116
+ result = py_unittest.pytest_pycollect_makeitem(collector, name, obj)
117
+ result.extra_keyword_matches.update(fixture.get_keywords(layer))
118
+ return result
119
+
120
+
121
+ def get_py_unittest(collector):
122
+ return collector.session.config.pluginmanager.getplugin('unittest')
@@ -0,0 +1,6 @@
1
+ def log_to_terminal(request, msg):
2
+ verbose = request.config.option.verbose > 0
3
+ if verbose:
4
+ reporter = request.config.pluginmanager.getplugin('terminalreporter')
5
+ reporter.ensure_newline()
6
+ reporter.write_line(msg)
File without changes
@@ -0,0 +1,3 @@
1
+ def pytest_ignore_collect(path, config):
2
+ if path.strpath.endswith('fixture'):
3
+ return True
File without changes
@@ -0,0 +1 @@
1
+ pytest_plugins = ('zopelayer', )
@@ -0,0 +1,13 @@
1
+ import unittest
2
+
3
+
4
+ class FooLayer(object):
5
+ __name__ = 'FooLayer'
6
+
7
+
8
+ class FooTest(unittest.TestCase):
9
+
10
+ layer = FooLayer()
11
+
12
+ def test_dummy(self):
13
+ self.assertEqual(1, 1)
@@ -0,0 +1,12 @@
1
+ from custom_fixture_name.test_core import FooLayer
2
+
3
+ import zope.pytestlayer.fixture
4
+
5
+
6
+ pytest_plugins = ('zopelayer', )
7
+
8
+
9
+ globals().update(zope.pytestlayer.fixture.create(
10
+ FooLayer,
11
+ class_fixture_name='foo_layer_class',
12
+ function_fixture_name='foo_layer'))
@@ -0,0 +1,38 @@
1
+ import unittest
2
+
3
+ from zope.pytestlayer.testing import log_to_terminal
4
+
5
+
6
+ class FooLayer(object):
7
+
8
+ @classmethod
9
+ def setUp(cls):
10
+ cls.layer_foo = 'layer foo'
11
+
12
+ @classmethod
13
+ def tearDown(cls):
14
+ del cls.layer_foo
15
+
16
+ @classmethod
17
+ def testSetUp(cls):
18
+ log_to_terminal(cls.pytest_request, 'testSetUp foo')
19
+ cls.test_foo = 'test foo'
20
+
21
+ @classmethod
22
+ def testTearDown(cls):
23
+ log_to_terminal(cls.pytest_request, 'testTearDown foo')
24
+ del cls.test_foo
25
+
26
+
27
+ def test_can_access_layer_via_fixture(foo_layer):
28
+ assert 'layer foo' == foo_layer.layer_foo
29
+ assert 'test foo' == foo_layer.test_foo
30
+
31
+
32
+ class FooTest(unittest.TestCase):
33
+
34
+ layer = FooLayer
35
+
36
+ def test_accesses_fixture_with_generated_name_for_layer(self):
37
+ self.assertEqual('layer foo', self.layer.layer_foo)
38
+ self.assertEqual('test foo', self.layer.test_foo)
@@ -0,0 +1 @@
1
+ pytest_plugins = ('zopelayer', )