File: test_collection.py

package info (click to toggle)
pytest-relaxed 2.0.2-5
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 212 kB
  • sloc: python: 960; makefile: 2
file content (478 lines) | stat: -rw-r--r-- 14,789 bytes parent folder | download | duplicates (2)
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
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
import re

from pytest import ExitCode, mark


# For 'testdir' fixture, mostly
pytest_plugins = "pytester"


class Test_pytest_collect_file:
    def test_only_loads_dot_py_files(self, testdir):
        testdir.makepyfile(
            somefile="""
            def hello_how_are_you():
                pass
        """
        )
        testdir.makefile(".txt", someotherfile="whatever")
        stdout = testdir.runpytest().stdout.str()
        # TODO: find it hard to believe pytest lacks strong "x in y" string
        # testing, but I cannot find any outside of fnmatch_lines (which is
        # specific to this testdir stuff, and also lacks an opposite...)
        assert "somefile.py" in stdout
        # This wouldn't actually even happen; we'd get an ImportError instead
        # as pytest tries importing 'someotherfile'. But eh.
        assert "whatever.txt" not in stdout

    def test_skips_underscored_files(self, testdir):
        testdir.makepyfile(
            hastests="""
            from _util import helper

            def hello_how_are_you():
                helper()
        """
        )
        testdir.makepyfile(
            _util="""
            def helper():
                pass
        """
        )
        # TODO: why Result.str() and not str(Result)? Seems unPythonic
        stdout = testdir.runpytest().stdout.str()
        assert "hastests.py" in stdout
        assert "_util.py" not in stdout

    def test_skips_underscored_directories(self, testdir):
        testdir.makepyfile(
            hello="""
            def hi_im_a_test_function():
                pass
"""
        )
        # NOTE: this appears to work due to impl details of pytester._makefile;
        # namely that the kwarg keys are handed directly to tmpdir.join(),
        # where tmpdir is a py.path.LocalPath.
        testdir.makepyfile(
            **{
                "_nope/yallo": """
            def hi_im_not_a_test_function():
                pass
"""
            }
        )
        stdout = testdir.runpytest("-v").stdout.str()
        assert "hi im a test function" in stdout
        assert "hi im not a test function" not in stdout

    def test_does_not_consume_conftest_files(self, testdir):
        testdir.makepyfile(
            actual_tests="""
            def hello_how_are_you():
                pass
        """
        )
        testdir.makepyfile(
            conftest="""
            def this_does_nothing_useful():
                pass
        """
        )
        stdout = testdir.runpytest().stdout.str()
        assert "actual_tests.py" in stdout
        assert "conftest.py" not in stdout


class TestRelaxedMixin:
    def test_selects_all_non_underscored_members(self, testdir):
        testdir.makepyfile(
            foo="""
            def hello_how_are_you():
                pass

            def _help_me_understand():
                pass

            class YupThisIsTests:
                def please_test_me_thx(self):
                    pass

                def _helper_method_hi(self):
                    pass

                class NestedTestClassAhoy:
                    def hello_I_am_a_test_method(self):
                        pass

                    def _but_I_am_not(self):
                        pass

                class _NotSureWhyYouWouldDoThisButWhatever:
                    def this_should_not_appear(self):
                        pass

            class _ForSomeReasonIAmDefinedHereButAmNotATest:
                def usually_you_would_just_import_this_but_okay(self):
                    pass
        """
        )
        stdout = testdir.runpytest("-v").stdout.str()
        for substring in (
            "hello how are you",
            "please test me thx",
            "hello I am a test method",
        ):
            assert substring in stdout
        for substring in (
            "help me understand",
            "helper method hi",
            "NotSureWhyYouWouldDoThisButWhatever",
            "ForSomeReasonIAmDefinedHereButAmNotATest",
        ):
            assert substring not in stdout

    def test_skips_setup_and_teardown(self, testdir):
        testdir.makepyfile(
            foo="""
            def setup():
                pass

            def teardown():
                pass

            def setup_method():
                pass

            def teardown_method():
                pass

            def actual_test_here():
                pass

            class Outer:
                def setup(self):
                    pass

                def teardown(self):
                    pass

                def setup_method(self):
                    pass

                def teardown_method(self):
                    pass

                def actual_nested_test_here(self):
                    pass
        """
        )
        stdout = testdir.runpytest("-v").stdout.str()
        # These skipped. Gotta regex them because the test name includes the
        # words 'setup' and 'teardown', heh.
        assert not re.match(r"^setup$", stdout)
        assert not re.match(r"^teardown$", stdout)
        assert not re.match(r"^setup_method$", stdout)
        assert not re.match(r"^teardown_method$", stdout)
        # Real tests not skipped
        assert "actual test here" in stdout
        assert "actual nested test here" in stdout

    def test_skips_pytest_fixtures(self, testdir):
        testdir.makepyfile(
            foo="""
            from pytest import fixture

            @fixture
            def pls_noload():
                yield

            def actual_test_here():
                pass
        """
        )
        stdout = testdir.runpytest("-v").stdout.str()
        assert "actual test here" in stdout
        # will be in stdout as a failure and warning if bug present
        assert "pls_noload" not in stdout

    def test_setup_given_inner_class_instances_when_inherited(self, testdir):
        # NOTE: without this functionality in place, we still see setup()
        # called on a per-test-method basis, but where 'self' is the outer
        # class, not the inner class! so anything actually touching 'self'
        # breaks.
        # TODO: should this pattern change to be something like a pytest
        # per-class autouse fixture method?
        # (https://docs.pytest.org/en/latest/fixture.html#autouse-fixtures-xunit-setup-on-steroids)
        testdir.makepyfile(
            foo="""
            class Outer:
                def setup_method(self):
                    self.some_attr = 17

                class inner:
                    def actual_nested_test(self):
                        assert self.some_attr == 17
        """
        )
        assert testdir.runpytest().ret is ExitCode.OK

    def test_setup_method_given_inner_class_instances(self, testdir):
        testdir.makepyfile(
            foo="""
            class Outer:
                def setup_method(self):
                    self.some_attr = 17

                class inner:
                    def actual_nested_test(self):
                        assert self.some_attr == 17
        """
        )
        assert testdir.runpytest().ret is ExitCode.OK


class TestSpecModule:
    def test_skips_non_callable_items(self, testdir):
        testdir.makepyfile(
            foo="""
            some_uncallable = 17

            def some_callable():
                pass
        """
        )
        stdout = testdir.runpytest("-v").stdout.str()
        assert "some_uncallable" not in stdout

    def test_skips_imported_objects(self, testdir):
        testdir.makepyfile(
            _util="""
            def helper():
                pass

            class Helper:
                pass

            class NewHelper:
                pass
        """
        )
        testdir.makepyfile(
            foo="""
            from _util import helper, Helper, NewHelper

            def a_test_is_me():
                pass
        """
        )
        stdout = testdir.runpytest("-v").stdout.str()
        assert "a test is me" in stdout
        assert "helper" not in stdout
        assert "Helper" not in stdout
        assert "NewHelper" not in stdout

    def test_does_not_warn_about_imported_names(self, testdir):
        # Trigger is something that appears callable but isn't a real function;
        # almost any callable class seems to suffice. (Real world triggers are
        # things like invoke/fabric Task objects.)
        # Can also be triggered if our collection is buggy and does not
        # explicitly reject imported classes (i.e. if we only reject funcs).
        testdir.makepyfile(
            _util="""
            class Callable:
                def __call__(self):
                    pass

            helper = Callable()

            class HelperClass:
                def __init__(self):
                    pass
        """
        )
        testdir.makepyfile(
            foo="""
            from _util import helper, HelperClass

            def a_test():
                pass
        """
        )
        stdout = testdir.runpytest("-sv").stdout.str()
        # TODO: more flexible test in case text changes? eh.
        for warning in (
            "cannot collect 'helper' because it is not a function",
            "cannot collect test class 'HelperClass'",
        ):
            assert warning not in stdout

    def test_replaces_class_tests_with_custom_recursing_classes(self, testdir):
        testdir.makepyfile(
            foo="""
            class Outer:
                class Middle:
                    class Inner:
                        def oh_look_an_actual_test_method(self):
                            pass
        """
        )
        stdout = testdir.runpytest("-v").stdout.str()
        expected = """
Outer

    Middle

        Inner

            oh look an actual test method
""".lstrip()
        assert expected in stdout

    def test_does_not_collect_test_prefixed_files(self, testdir):
        # Incidentally also tests display stripping; the display test suite has
        # explicit tests for that too tho.
        testdir.makepyfile(
            test_something="""
            import unittest

            class TestMyStuff(unittest.TestCase):
                def test_things(self):
                    pass
        """
        )
        stdout = testdir.runpytest("-v").stdout.str()
        expected = """
MyStuff

    things

""".lstrip()
        assert expected in stdout
        # Make sure no warnings were emitted; much of the time, our collection
        # bits will cause nasty warnings if they end up consuming unittest
        # stuff or otherwise doubling up on already-collected objects.
        assert "warnings summary" not in stdout

    @mark.skip
    def test_correctly_handles_marked_test_cases(self, testdir):
        # I.e. @pytest.mark.someflag objects at the class level...figure out
        # how real collectors handle these exactly? the "actual" test class we
        # normally care about is inside of it.
        pass


class TestSpecClass:
    def test_methods_self_objects_exhibit_class_attributes(self, testdir):
        # Mostly a sanity test; pytest seems to get out of the way enough that
        # the test is truly a bound method & the 'self' is truly an instance of
        # the class.
        testdir.makepyfile(
            foo="""
            class MyClass:
                an_attr = 5

                def some_test(self):
                    assert hasattr(self, 'an_attr')
                    assert self.an_attr == 5
        """
        )
        # TODO: first thought was "why is this not automatic?", then realized
        # "duh, it'd be annoying if you wanted to test failure related behavior
        # a lot"...but still want some slightly nicer helper I think
        assert testdir.runpytest().ret is ExitCode.OK

    def test_nested_self_objects_exhibit_parent_attributes(self, testdir):
        # TODO: really starting to think going back to 'real' fixture files
        # makes more sense; this is all real python code and is eval'd as such,
        # but it is only editable and viewable as a string. No highlighting.
        testdir.makepyfile(
            foo="""
            class MyClass:
                an_attr = 5

                class Inner:
                    def inner_test(self):
                        assert hasattr(self, 'an_attr')
                        assert self.an_attr == 5
        """
        )
        assert testdir.runpytest().ret is ExitCode.OK

    def test_nesting_is_infinite(self, testdir):
        testdir.makepyfile(
            foo="""
            class MyClass:
                an_attr = 5

                class Inner:
                    class Deeper:
                        class EvenDeeper:
                            def innermost_test(self):
                                assert hasattr(self, 'an_attr')
                                assert self.an_attr == 5
        """
        )
        assert testdir.runpytest().ret is ExitCode.OK

    def test_overriding_works_naturally(self, testdir):
        testdir.makepyfile(
            foo="""
            class MyClass:
                an_attr = 5

                class Inner:
                    an_attr = 7

                    def inner_test(self):
                        assert self.an_attr == 7
        """
        )
        assert testdir.runpytest().ret is ExitCode.OK

    def test_normal_methods_from_outer_classes_are_not_copied(self, testdir):
        testdir.makepyfile(
            foo="""
            class MyClass:
                def outer_test(self):
                    pass

                class Inner:
                    def inner_test(self):
                        assert not hasattr(self, 'outer_test')
        """
        )
        assert testdir.runpytest().ret is ExitCode.OK

    def test_private_methods_from_outer_classes_are_copied(self, testdir):
        testdir.makepyfile(
            foo="""
            class MyClass:
                def outer_test(self):
                    pass

                def _outer_helper(self):
                    pass

                class Inner:
                    def inner_test(self):
                        assert not hasattr(self, 'outer_test')
                        assert hasattr(self, '_outer_helper')
        """
        )
        assert testdir.runpytest().ret is ExitCode.OK

    def test_module_contents_are_not_copied_into_top_level_classes(
        self, testdir
    ):
        testdir.makepyfile(
            foo="""
            module_constant = 17

            class MyClass:
                def outer_test(self):
                    assert not hasattr(self, 'module_constant')
        """
        )
        assert testdir.runpytest().ret is ExitCode.OK