1import os 2import sys 3from unittest import mock 4 5import pytest 6from _pytest.config import ExitCode 7from _pytest.mark import MarkGenerator as Mark 8from _pytest.mark.structures import EMPTY_PARAMETERSET_OPTION 9from _pytest.nodes import Collector 10from _pytest.nodes import Node 11 12 13class TestMark: 14 @pytest.mark.parametrize("attr", ["mark", "param"]) 15 @pytest.mark.parametrize("modulename", ["py.test", "pytest"]) 16 def test_pytest_exists_in_namespace_all(self, attr: str, modulename: str) -> None: 17 module = sys.modules[modulename] 18 assert attr in module.__all__ # type: ignore 19 20 def test_pytest_mark_notcallable(self) -> None: 21 mark = Mark() 22 with pytest.raises(TypeError): 23 mark() # type: ignore[operator] 24 25 def test_mark_with_param(self): 26 def some_function(abc): 27 pass 28 29 class SomeClass: 30 pass 31 32 assert pytest.mark.foo(some_function) is some_function 33 marked_with_args = pytest.mark.foo.with_args(some_function) 34 assert marked_with_args is not some_function # type: ignore[comparison-overlap] 35 36 assert pytest.mark.foo(SomeClass) is SomeClass 37 assert pytest.mark.foo.with_args(SomeClass) is not SomeClass # type: ignore[comparison-overlap] 38 39 def test_pytest_mark_name_starts_with_underscore(self): 40 mark = Mark() 41 with pytest.raises(AttributeError): 42 mark._some_name 43 44 45def test_marked_class_run_twice(testdir): 46 """Test fails file is run twice that contains marked class. 47 See issue#683. 48 """ 49 py_file = testdir.makepyfile( 50 """ 51 import pytest 52 @pytest.mark.parametrize('abc', [1, 2, 3]) 53 class Test1(object): 54 def test_1(self, abc): 55 assert abc in [1, 2, 3] 56 """ 57 ) 58 file_name = os.path.basename(py_file.strpath) 59 rec = testdir.inline_run(file_name, file_name) 60 rec.assertoutcome(passed=6) 61 62 63def test_ini_markers(testdir): 64 testdir.makeini( 65 """ 66 [pytest] 67 markers = 68 a1: this is a webtest marker 69 a2: this is a smoke marker 70 """ 71 ) 72 testdir.makepyfile( 73 """ 74 def test_markers(pytestconfig): 75 markers = pytestconfig.getini("markers") 76 print(markers) 77 assert len(markers) >= 2 78 assert markers[0].startswith("a1:") 79 assert markers[1].startswith("a2:") 80 """ 81 ) 82 rec = testdir.inline_run() 83 rec.assertoutcome(passed=1) 84 85 86def test_markers_option(testdir): 87 testdir.makeini( 88 """ 89 [pytest] 90 markers = 91 a1: this is a webtest marker 92 a1some: another marker 93 nodescription 94 """ 95 ) 96 result = testdir.runpytest("--markers") 97 result.stdout.fnmatch_lines( 98 ["*a1*this is a webtest*", "*a1some*another marker", "*nodescription*"] 99 ) 100 101 102def test_ini_markers_whitespace(testdir): 103 testdir.makeini( 104 """ 105 [pytest] 106 markers = 107 a1 : this is a whitespace marker 108 """ 109 ) 110 testdir.makepyfile( 111 """ 112 import pytest 113 114 @pytest.mark.a1 115 def test_markers(): 116 assert True 117 """ 118 ) 119 rec = testdir.inline_run("--strict-markers", "-m", "a1") 120 rec.assertoutcome(passed=1) 121 122 123def test_marker_without_description(testdir): 124 testdir.makefile( 125 ".cfg", 126 setup=""" 127 [tool:pytest] 128 markers=slow 129 """, 130 ) 131 testdir.makeconftest( 132 """ 133 import pytest 134 pytest.mark.xfail('FAIL') 135 """ 136 ) 137 ftdir = testdir.mkdir("ft1_dummy") 138 testdir.tmpdir.join("conftest.py").move(ftdir.join("conftest.py")) 139 rec = testdir.runpytest("--strict-markers") 140 rec.assert_outcomes() 141 142 143def test_markers_option_with_plugin_in_current_dir(testdir): 144 testdir.makeconftest('pytest_plugins = "flip_flop"') 145 testdir.makepyfile( 146 flip_flop="""\ 147 def pytest_configure(config): 148 config.addinivalue_line("markers", "flip:flop") 149 150 def pytest_generate_tests(metafunc): 151 try: 152 mark = metafunc.function.flipper 153 except AttributeError: 154 return 155 metafunc.parametrize("x", (10, 20))""" 156 ) 157 testdir.makepyfile( 158 """\ 159 import pytest 160 @pytest.mark.flipper 161 def test_example(x): 162 assert x""" 163 ) 164 165 result = testdir.runpytest("--markers") 166 result.stdout.fnmatch_lines(["*flip*flop*"]) 167 168 169def test_mark_on_pseudo_function(testdir): 170 testdir.makepyfile( 171 """ 172 import pytest 173 174 @pytest.mark.r(lambda x: 0/0) 175 def test_hello(): 176 pass 177 """ 178 ) 179 reprec = testdir.inline_run() 180 reprec.assertoutcome(passed=1) 181 182 183@pytest.mark.parametrize("option_name", ["--strict-markers", "--strict"]) 184def test_strict_prohibits_unregistered_markers(testdir, option_name): 185 testdir.makepyfile( 186 """ 187 import pytest 188 @pytest.mark.unregisteredmark 189 def test_hello(): 190 pass 191 """ 192 ) 193 result = testdir.runpytest(option_name) 194 assert result.ret != 0 195 result.stdout.fnmatch_lines( 196 ["'unregisteredmark' not found in `markers` configuration option"] 197 ) 198 199 200@pytest.mark.parametrize( 201 ("expr", "expected_passed"), 202 [ 203 ("xyz", ["test_one"]), 204 ("((( xyz)) )", ["test_one"]), 205 ("not not xyz", ["test_one"]), 206 ("xyz and xyz2", []), 207 ("xyz2", ["test_two"]), 208 ("xyz or xyz2", ["test_one", "test_two"]), 209 ], 210) 211def test_mark_option(expr: str, expected_passed: str, testdir) -> None: 212 testdir.makepyfile( 213 """ 214 import pytest 215 @pytest.mark.xyz 216 def test_one(): 217 pass 218 @pytest.mark.xyz2 219 def test_two(): 220 pass 221 """ 222 ) 223 rec = testdir.inline_run("-m", expr) 224 passed, skipped, fail = rec.listoutcomes() 225 passed = [x.nodeid.split("::")[-1] for x in passed] 226 assert passed == expected_passed 227 228 229@pytest.mark.parametrize( 230 ("expr", "expected_passed"), 231 [("interface", ["test_interface"]), ("not interface", ["test_nointer"])], 232) 233def test_mark_option_custom(expr: str, expected_passed: str, testdir) -> None: 234 testdir.makeconftest( 235 """ 236 import pytest 237 def pytest_collection_modifyitems(items): 238 for item in items: 239 if "interface" in item.nodeid: 240 item.add_marker(pytest.mark.interface) 241 """ 242 ) 243 testdir.makepyfile( 244 """ 245 def test_interface(): 246 pass 247 def test_nointer(): 248 pass 249 """ 250 ) 251 rec = testdir.inline_run("-m", expr) 252 passed, skipped, fail = rec.listoutcomes() 253 passed = [x.nodeid.split("::")[-1] for x in passed] 254 assert passed == expected_passed 255 256 257@pytest.mark.parametrize( 258 ("expr", "expected_passed"), 259 [ 260 ("interface", ["test_interface"]), 261 ("not interface", ["test_nointer", "test_pass", "test_1", "test_2"]), 262 ("pass", ["test_pass"]), 263 ("not pass", ["test_interface", "test_nointer", "test_1", "test_2"]), 264 ("not not not (pass)", ["test_interface", "test_nointer", "test_1", "test_2"]), 265 ("1 or 2", ["test_1", "test_2"]), 266 ("not (1 or 2)", ["test_interface", "test_nointer", "test_pass"]), 267 ], 268) 269def test_keyword_option_custom(expr: str, expected_passed: str, testdir) -> None: 270 testdir.makepyfile( 271 """ 272 def test_interface(): 273 pass 274 def test_nointer(): 275 pass 276 def test_pass(): 277 pass 278 def test_1(): 279 pass 280 def test_2(): 281 pass 282 """ 283 ) 284 rec = testdir.inline_run("-k", expr) 285 passed, skipped, fail = rec.listoutcomes() 286 passed = [x.nodeid.split("::")[-1] for x in passed] 287 assert passed == expected_passed 288 289 290def test_keyword_option_considers_mark(testdir): 291 testdir.copy_example("marks/marks_considered_keywords") 292 rec = testdir.inline_run("-k", "foo") 293 passed = rec.listoutcomes()[0] 294 assert len(passed) == 1 295 296 297@pytest.mark.parametrize( 298 ("expr", "expected_passed"), 299 [ 300 ("None", ["test_func[None]"]), 301 ("[1.3]", ["test_func[1.3]"]), 302 ("2-3", ["test_func[2-3]"]), 303 ], 304) 305def test_keyword_option_parametrize(expr: str, expected_passed: str, testdir) -> None: 306 testdir.makepyfile( 307 """ 308 import pytest 309 @pytest.mark.parametrize("arg", [None, 1.3, "2-3"]) 310 def test_func(arg): 311 pass 312 """ 313 ) 314 rec = testdir.inline_run("-k", expr) 315 passed, skipped, fail = rec.listoutcomes() 316 passed = [x.nodeid.split("::")[-1] for x in passed] 317 assert passed == expected_passed 318 319 320def test_parametrize_with_module(testdir): 321 testdir.makepyfile( 322 """ 323 import pytest 324 @pytest.mark.parametrize("arg", [pytest,]) 325 def test_func(arg): 326 pass 327 """ 328 ) 329 rec = testdir.inline_run() 330 passed, skipped, fail = rec.listoutcomes() 331 expected_id = "test_func[" + pytest.__name__ + "]" 332 assert passed[0].nodeid.split("::")[-1] == expected_id 333 334 335@pytest.mark.parametrize( 336 ("expr", "expected_error"), 337 [ 338 ( 339 "foo or", 340 "at column 7: expected not OR left parenthesis OR identifier; got end of input", 341 ), 342 ( 343 "foo or or", 344 "at column 8: expected not OR left parenthesis OR identifier; got or", 345 ), 346 ("(foo", "at column 5: expected right parenthesis; got end of input",), 347 ("foo bar", "at column 5: expected end of input; got identifier",), 348 ( 349 "or or", 350 "at column 1: expected not OR left parenthesis OR identifier; got or", 351 ), 352 ( 353 "not or", 354 "at column 5: expected not OR left parenthesis OR identifier; got or", 355 ), 356 ], 357) 358def test_keyword_option_wrong_arguments( 359 expr: str, expected_error: str, testdir, capsys 360) -> None: 361 testdir.makepyfile( 362 """ 363 def test_func(arg): 364 pass 365 """ 366 ) 367 testdir.inline_run("-k", expr) 368 err = capsys.readouterr().err 369 assert expected_error in err 370 371 372def test_parametrized_collected_from_command_line(testdir): 373 """Parametrized test not collected if test named specified in command 374 line issue#649.""" 375 py_file = testdir.makepyfile( 376 """ 377 import pytest 378 @pytest.mark.parametrize("arg", [None, 1.3, "2-3"]) 379 def test_func(arg): 380 pass 381 """ 382 ) 383 file_name = os.path.basename(py_file.strpath) 384 rec = testdir.inline_run(file_name + "::" + "test_func") 385 rec.assertoutcome(passed=3) 386 387 388def test_parametrized_collect_with_wrong_args(testdir): 389 """Test collect parametrized func with wrong number of args.""" 390 py_file = testdir.makepyfile( 391 """ 392 import pytest 393 394 @pytest.mark.parametrize('foo, bar', [(1, 2, 3)]) 395 def test_func(foo, bar): 396 pass 397 """ 398 ) 399 400 result = testdir.runpytest(py_file) 401 result.stdout.fnmatch_lines( 402 [ 403 'test_parametrized_collect_with_wrong_args.py::test_func: in "parametrize" the number of names (2):', 404 " ['foo', 'bar']", 405 "must be equal to the number of values (3):", 406 " (1, 2, 3)", 407 ] 408 ) 409 410 411def test_parametrized_with_kwargs(testdir): 412 """Test collect parametrized func with wrong number of args.""" 413 py_file = testdir.makepyfile( 414 """ 415 import pytest 416 417 @pytest.fixture(params=[1,2]) 418 def a(request): 419 return request.param 420 421 @pytest.mark.parametrize(argnames='b', argvalues=[1, 2]) 422 def test_func(a, b): 423 pass 424 """ 425 ) 426 427 result = testdir.runpytest(py_file) 428 assert result.ret == 0 429 430 431def test_parametrize_iterator(testdir): 432 """`parametrize` should work with generators (#5354).""" 433 py_file = testdir.makepyfile( 434 """\ 435 import pytest 436 437 def gen(): 438 yield 1 439 yield 2 440 yield 3 441 442 @pytest.mark.parametrize('a', gen()) 443 def test(a): 444 assert a >= 1 445 """ 446 ) 447 result = testdir.runpytest(py_file) 448 assert result.ret == 0 449 # should not skip any tests 450 result.stdout.fnmatch_lines(["*3 passed*"]) 451 452 453class TestFunctional: 454 def test_merging_markers_deep(self, testdir): 455 # issue 199 - propagate markers into nested classes 456 p = testdir.makepyfile( 457 """ 458 import pytest 459 class TestA(object): 460 pytestmark = pytest.mark.a 461 def test_b(self): 462 assert True 463 class TestC(object): 464 # this one didn't get marked 465 def test_d(self): 466 assert True 467 """ 468 ) 469 items, rec = testdir.inline_genitems(p) 470 for item in items: 471 print(item, item.keywords) 472 assert [x for x in item.iter_markers() if x.name == "a"] 473 474 def test_mark_decorator_subclass_does_not_propagate_to_base(self, testdir): 475 p = testdir.makepyfile( 476 """ 477 import pytest 478 479 @pytest.mark.a 480 class Base(object): pass 481 482 @pytest.mark.b 483 class Test1(Base): 484 def test_foo(self): pass 485 486 class Test2(Base): 487 def test_bar(self): pass 488 """ 489 ) 490 items, rec = testdir.inline_genitems(p) 491 self.assert_markers(items, test_foo=("a", "b"), test_bar=("a",)) 492 493 def test_mark_should_not_pass_to_siebling_class(self, testdir): 494 """#568""" 495 p = testdir.makepyfile( 496 """ 497 import pytest 498 499 class TestBase(object): 500 def test_foo(self): 501 pass 502 503 @pytest.mark.b 504 class TestSub(TestBase): 505 pass 506 507 508 class TestOtherSub(TestBase): 509 pass 510 511 """ 512 ) 513 items, rec = testdir.inline_genitems(p) 514 base_item, sub_item, sub_item_other = items 515 print(items, [x.nodeid for x in items]) 516 # new api segregates 517 assert not list(base_item.iter_markers(name="b")) 518 assert not list(sub_item_other.iter_markers(name="b")) 519 assert list(sub_item.iter_markers(name="b")) 520 521 def test_mark_decorator_baseclasses_merged(self, testdir): 522 p = testdir.makepyfile( 523 """ 524 import pytest 525 526 @pytest.mark.a 527 class Base(object): pass 528 529 @pytest.mark.b 530 class Base2(Base): pass 531 532 @pytest.mark.c 533 class Test1(Base2): 534 def test_foo(self): pass 535 536 class Test2(Base2): 537 @pytest.mark.d 538 def test_bar(self): pass 539 """ 540 ) 541 items, rec = testdir.inline_genitems(p) 542 self.assert_markers(items, test_foo=("a", "b", "c"), test_bar=("a", "b", "d")) 543 544 def test_mark_closest(self, testdir): 545 p = testdir.makepyfile( 546 """ 547 import pytest 548 549 @pytest.mark.c(location="class") 550 class Test: 551 @pytest.mark.c(location="function") 552 def test_has_own(self): 553 pass 554 555 def test_has_inherited(self): 556 pass 557 558 """ 559 ) 560 items, rec = testdir.inline_genitems(p) 561 has_own, has_inherited = items 562 assert has_own.get_closest_marker("c").kwargs == {"location": "function"} 563 assert has_inherited.get_closest_marker("c").kwargs == {"location": "class"} 564 assert has_own.get_closest_marker("missing") is None 565 566 def test_mark_with_wrong_marker(self, testdir): 567 reprec = testdir.inline_runsource( 568 """ 569 import pytest 570 class pytestmark(object): 571 pass 572 def test_func(): 573 pass 574 """ 575 ) 576 values = reprec.getfailedcollections() 577 assert len(values) == 1 578 assert "TypeError" in str(values[0].longrepr) 579 580 def test_mark_dynamically_in_funcarg(self, testdir): 581 testdir.makeconftest( 582 """ 583 import pytest 584 @pytest.fixture 585 def arg(request): 586 request.applymarker(pytest.mark.hello) 587 def pytest_terminal_summary(terminalreporter): 588 values = terminalreporter.stats['passed'] 589 terminalreporter._tw.line("keyword: %s" % values[0].keywords) 590 """ 591 ) 592 testdir.makepyfile( 593 """ 594 def test_func(arg): 595 pass 596 """ 597 ) 598 result = testdir.runpytest() 599 result.stdout.fnmatch_lines(["keyword: *hello*"]) 600 601 def test_no_marker_match_on_unmarked_names(self, testdir): 602 p = testdir.makepyfile( 603 """ 604 import pytest 605 @pytest.mark.shouldmatch 606 def test_marked(): 607 assert 1 608 609 def test_unmarked(): 610 assert 1 611 """ 612 ) 613 reprec = testdir.inline_run("-m", "test_unmarked", p) 614 passed, skipped, failed = reprec.listoutcomes() 615 assert len(passed) + len(skipped) + len(failed) == 0 616 dlist = reprec.getcalls("pytest_deselected") 617 deselected_tests = dlist[0].items 618 assert len(deselected_tests) == 2 619 620 def test_keywords_at_node_level(self, testdir): 621 testdir.makepyfile( 622 """ 623 import pytest 624 @pytest.fixture(scope="session", autouse=True) 625 def some(request): 626 request.keywords["hello"] = 42 627 assert "world" not in request.keywords 628 629 @pytest.fixture(scope="function", autouse=True) 630 def funcsetup(request): 631 assert "world" in request.keywords 632 assert "hello" in request.keywords 633 634 @pytest.mark.world 635 def test_function(): 636 pass 637 """ 638 ) 639 reprec = testdir.inline_run() 640 reprec.assertoutcome(passed=1) 641 642 def test_keyword_added_for_session(self, testdir): 643 testdir.makeconftest( 644 """ 645 import pytest 646 def pytest_collection_modifyitems(session): 647 session.add_marker("mark1") 648 session.add_marker(pytest.mark.mark2) 649 session.add_marker(pytest.mark.mark3) 650 pytest.raises(ValueError, lambda: 651 session.add_marker(10)) 652 """ 653 ) 654 testdir.makepyfile( 655 """ 656 def test_some(request): 657 assert "mark1" in request.keywords 658 assert "mark2" in request.keywords 659 assert "mark3" in request.keywords 660 assert 10 not in request.keywords 661 marker = request.node.get_closest_marker("mark1") 662 assert marker.name == "mark1" 663 assert marker.args == () 664 assert marker.kwargs == {} 665 """ 666 ) 667 reprec = testdir.inline_run("-m", "mark1") 668 reprec.assertoutcome(passed=1) 669 670 def assert_markers(self, items, **expected): 671 """Assert that given items have expected marker names applied to them. 672 expected should be a dict of (item name -> seq of expected marker names). 673 674 Note: this could be moved to ``testdir`` if proven to be useful 675 to other modules. 676 """ 677 items = {x.name: x for x in items} 678 for name, expected_markers in expected.items(): 679 markers = {m.name for m in items[name].iter_markers()} 680 assert markers == set(expected_markers) 681 682 @pytest.mark.filterwarnings("ignore") 683 def test_mark_from_parameters(self, testdir): 684 """#1540""" 685 testdir.makepyfile( 686 """ 687 import pytest 688 689 pytestmark = pytest.mark.skipif(True, reason='skip all') 690 691 # skipifs inside fixture params 692 params = [pytest.mark.skipif(False, reason='dont skip')('parameter')] 693 694 695 @pytest.fixture(params=params) 696 def parameter(request): 697 return request.param 698 699 700 def test_1(parameter): 701 assert True 702 """ 703 ) 704 reprec = testdir.inline_run() 705 reprec.assertoutcome(skipped=1) 706 707 def test_reevaluate_dynamic_expr(self, testdir): 708 """#7360""" 709 py_file1 = testdir.makepyfile( 710 test_reevaluate_dynamic_expr1=""" 711 import pytest 712 713 skip = True 714 715 @pytest.mark.skipif("skip") 716 def test_should_skip(): 717 assert True 718 """ 719 ) 720 py_file2 = testdir.makepyfile( 721 test_reevaluate_dynamic_expr2=""" 722 import pytest 723 724 skip = False 725 726 @pytest.mark.skipif("skip") 727 def test_should_not_skip(): 728 assert True 729 """ 730 ) 731 732 file_name1 = os.path.basename(py_file1.strpath) 733 file_name2 = os.path.basename(py_file2.strpath) 734 reprec = testdir.inline_run(file_name1, file_name2) 735 reprec.assertoutcome(passed=1, skipped=1) 736 737 738class TestKeywordSelection: 739 def test_select_simple(self, testdir): 740 file_test = testdir.makepyfile( 741 """ 742 def test_one(): 743 assert 0 744 class TestClass(object): 745 def test_method_one(self): 746 assert 42 == 43 747 """ 748 ) 749 750 def check(keyword, name): 751 reprec = testdir.inline_run("-s", "-k", keyword, file_test) 752 passed, skipped, failed = reprec.listoutcomes() 753 assert len(failed) == 1 754 assert failed[0].nodeid.split("::")[-1] == name 755 assert len(reprec.getcalls("pytest_deselected")) == 1 756 757 for keyword in ["test_one", "est_on"]: 758 check(keyword, "test_one") 759 check("TestClass and test", "test_method_one") 760 761 @pytest.mark.parametrize( 762 "keyword", 763 [ 764 "xxx", 765 "xxx and test_2", 766 "TestClass", 767 "xxx and not test_1", 768 "TestClass and test_2", 769 "xxx and TestClass and test_2", 770 ], 771 ) 772 def test_select_extra_keywords(self, testdir, keyword): 773 p = testdir.makepyfile( 774 test_select=""" 775 def test_1(): 776 pass 777 class TestClass(object): 778 def test_2(self): 779 pass 780 """ 781 ) 782 testdir.makepyfile( 783 conftest=""" 784 import pytest 785 @pytest.hookimpl(hookwrapper=True) 786 def pytest_pycollect_makeitem(name): 787 outcome = yield 788 if name == "TestClass": 789 item = outcome.get_result() 790 item.extra_keyword_matches.add("xxx") 791 """ 792 ) 793 reprec = testdir.inline_run(p.dirpath(), "-s", "-k", keyword) 794 print("keyword", repr(keyword)) 795 passed, skipped, failed = reprec.listoutcomes() 796 assert len(passed) == 1 797 assert passed[0].nodeid.endswith("test_2") 798 dlist = reprec.getcalls("pytest_deselected") 799 assert len(dlist) == 1 800 assert dlist[0].items[0].name == "test_1" 801 802 def test_select_starton(self, testdir): 803 threepass = testdir.makepyfile( 804 test_threepass=""" 805 def test_one(): assert 1 806 def test_two(): assert 1 807 def test_three(): assert 1 808 """ 809 ) 810 reprec = testdir.inline_run("-k", "test_two:", threepass) 811 passed, skipped, failed = reprec.listoutcomes() 812 assert len(passed) == 2 813 assert not failed 814 dlist = reprec.getcalls("pytest_deselected") 815 assert len(dlist) == 1 816 item = dlist[0].items[0] 817 assert item.name == "test_one" 818 819 def test_keyword_extra(self, testdir): 820 p = testdir.makepyfile( 821 """ 822 def test_one(): 823 assert 0 824 test_one.mykeyword = True 825 """ 826 ) 827 reprec = testdir.inline_run("-k", "mykeyword", p) 828 passed, skipped, failed = reprec.countoutcomes() 829 assert failed == 1 830 831 @pytest.mark.xfail 832 def test_keyword_extra_dash(self, testdir): 833 p = testdir.makepyfile( 834 """ 835 def test_one(): 836 assert 0 837 test_one.mykeyword = True 838 """ 839 ) 840 # with argparse the argument to an option cannot 841 # start with '-' 842 reprec = testdir.inline_run("-k", "-mykeyword", p) 843 passed, skipped, failed = reprec.countoutcomes() 844 assert passed + skipped + failed == 0 845 846 @pytest.mark.parametrize( 847 "keyword", ["__", "+", ".."], 848 ) 849 def test_no_magic_values(self, testdir, keyword: str) -> None: 850 """Make sure the tests do not match on magic values, 851 no double underscored values, like '__dict__' and '+'. 852 """ 853 p = testdir.makepyfile( 854 """ 855 def test_one(): assert 1 856 """ 857 ) 858 859 reprec = testdir.inline_run("-k", keyword, p) 860 passed, skipped, failed = reprec.countoutcomes() 861 dlist = reprec.getcalls("pytest_deselected") 862 assert passed + skipped + failed == 0 863 deselected_tests = dlist[0].items 864 assert len(deselected_tests) == 1 865 866 def test_no_match_directories_outside_the_suite(self, testdir): 867 """`-k` should not match against directories containing the test suite (#7040).""" 868 test_contents = """ 869 def test_aaa(): pass 870 def test_ddd(): pass 871 """ 872 testdir.makepyfile( 873 **{"ddd/tests/__init__.py": "", "ddd/tests/test_foo.py": test_contents} 874 ) 875 876 def get_collected_names(*args): 877 _, rec = testdir.inline_genitems(*args) 878 calls = rec.getcalls("pytest_collection_finish") 879 assert len(calls) == 1 880 return [x.name for x in calls[0].session.items] 881 882 # sanity check: collect both tests in normal runs 883 assert get_collected_names() == ["test_aaa", "test_ddd"] 884 885 # do not collect anything based on names outside the collection tree 886 assert get_collected_names("-k", testdir.tmpdir.basename) == [] 887 888 # "-k ddd" should only collect "test_ddd", but not 889 # 'test_aaa' just because one of its parent directories is named "ddd"; 890 # this was matched previously because Package.name would contain the full path 891 # to the package 892 assert get_collected_names("-k", "ddd") == ["test_ddd"] 893 894 895class TestMarkDecorator: 896 @pytest.mark.parametrize( 897 "lhs, rhs, expected", 898 [ 899 (pytest.mark.foo(), pytest.mark.foo(), True), 900 (pytest.mark.foo(), pytest.mark.bar(), False), 901 (pytest.mark.foo(), "bar", False), 902 ("foo", pytest.mark.bar(), False), 903 ], 904 ) 905 def test__eq__(self, lhs, rhs, expected): 906 assert (lhs == rhs) == expected 907 908 def test_aliases(self) -> None: 909 md = pytest.mark.foo(1, "2", three=3) 910 assert md.name == "foo" 911 assert md.args == (1, "2") 912 assert md.kwargs == {"three": 3} 913 914 915@pytest.mark.parametrize("mark", [None, "", "skip", "xfail"]) 916def test_parameterset_for_parametrize_marks(testdir, mark): 917 if mark is not None: 918 testdir.makeini( 919 """ 920 [pytest] 921 {}={} 922 """.format( 923 EMPTY_PARAMETERSET_OPTION, mark 924 ) 925 ) 926 927 config = testdir.parseconfig() 928 from _pytest.mark import pytest_configure, get_empty_parameterset_mark 929 930 pytest_configure(config) 931 result_mark = get_empty_parameterset_mark(config, ["a"], all) 932 if mark in (None, ""): 933 # normalize to the requested name 934 mark = "skip" 935 assert result_mark.name == mark 936 assert result_mark.kwargs["reason"].startswith("got empty parameter set ") 937 if mark == "xfail": 938 assert result_mark.kwargs.get("run") is False 939 940 941def test_parameterset_for_fail_at_collect(testdir): 942 testdir.makeini( 943 """ 944 [pytest] 945 {}=fail_at_collect 946 """.format( 947 EMPTY_PARAMETERSET_OPTION 948 ) 949 ) 950 951 config = testdir.parseconfig() 952 from _pytest.mark import pytest_configure, get_empty_parameterset_mark 953 954 pytest_configure(config) 955 956 with pytest.raises( 957 Collector.CollectError, 958 match=r"Empty parameter set in 'pytest_configure' at line \d\d+", 959 ): 960 get_empty_parameterset_mark(config, ["a"], pytest_configure) 961 962 p1 = testdir.makepyfile( 963 """ 964 import pytest 965 966 @pytest.mark.parametrize("empty", []) 967 def test(): 968 pass 969 """ 970 ) 971 result = testdir.runpytest(str(p1)) 972 result.stdout.fnmatch_lines( 973 [ 974 "collected 0 items / 1 error", 975 "* ERROR collecting test_parameterset_for_fail_at_collect.py *", 976 "Empty parameter set in 'test' at line 3", 977 "*= 1 error in *", 978 ] 979 ) 980 assert result.ret == ExitCode.INTERRUPTED 981 982 983def test_parameterset_for_parametrize_bad_markname(testdir): 984 with pytest.raises(pytest.UsageError): 985 test_parameterset_for_parametrize_marks(testdir, "bad") 986 987 988def test_mark_expressions_no_smear(testdir): 989 testdir.makepyfile( 990 """ 991 import pytest 992 993 class BaseTests(object): 994 def test_something(self): 995 pass 996 997 @pytest.mark.FOO 998 class TestFooClass(BaseTests): 999 pass 1000 1001 @pytest.mark.BAR 1002 class TestBarClass(BaseTests): 1003 pass 1004 """ 1005 ) 1006 1007 reprec = testdir.inline_run("-m", "FOO") 1008 passed, skipped, failed = reprec.countoutcomes() 1009 dlist = reprec.getcalls("pytest_deselected") 1010 assert passed == 1 1011 assert skipped == failed == 0 1012 deselected_tests = dlist[0].items 1013 assert len(deselected_tests) == 1 1014 1015 # todo: fixed 1016 # keywords smear - expected behaviour 1017 # reprec_keywords = testdir.inline_run("-k", "FOO") 1018 # passed_k, skipped_k, failed_k = reprec_keywords.countoutcomes() 1019 # assert passed_k == 2 1020 # assert skipped_k == failed_k == 0 1021 1022 1023def test_addmarker_order(): 1024 session = mock.Mock() 1025 session.own_markers = [] 1026 session.parent = None 1027 session.nodeid = "" 1028 node = Node.from_parent(session, name="Test") 1029 node.add_marker("foo") 1030 node.add_marker("bar") 1031 node.add_marker("baz", append=False) 1032 extracted = [x.name for x in node.iter_markers()] 1033 assert extracted == ["baz", "foo", "bar"] 1034 1035 1036@pytest.mark.filterwarnings("ignore") 1037def test_markers_from_parametrize(testdir): 1038 """#3605""" 1039 testdir.makepyfile( 1040 """ 1041 import pytest 1042 1043 first_custom_mark = pytest.mark.custom_marker 1044 custom_mark = pytest.mark.custom_mark 1045 @pytest.fixture(autouse=True) 1046 def trigger(request): 1047 custom_mark = list(request.node.iter_markers('custom_mark')) 1048 print("Custom mark %s" % custom_mark) 1049 1050 @custom_mark("custom mark non parametrized") 1051 def test_custom_mark_non_parametrized(): 1052 print("Hey from test") 1053 1054 @pytest.mark.parametrize( 1055 "obj_type", 1056 [ 1057 first_custom_mark("first custom mark")("template"), 1058 pytest.param( # Think this should be recommended way? 1059 "disk", 1060 marks=custom_mark('custom mark1') 1061 ), 1062 custom_mark("custom mark2")("vm"), # Tried also this 1063 ] 1064 ) 1065 def test_custom_mark_parametrized(obj_type): 1066 print("obj_type is:", obj_type) 1067 """ 1068 ) 1069 1070 result = testdir.runpytest() 1071 result.assert_outcomes(passed=4) 1072 1073 1074def test_pytest_param_id_requires_string() -> None: 1075 with pytest.raises(TypeError) as excinfo: 1076 pytest.param(id=True) # type: ignore[arg-type] 1077 (msg,) = excinfo.value.args 1078 assert msg == "Expected id to be a string, got <class 'bool'>: True" 1079 1080 1081@pytest.mark.parametrize("s", (None, "hello world")) 1082def test_pytest_param_id_allows_none_or_string(s): 1083 assert pytest.param(id=s) 1084 1085 1086@pytest.mark.parametrize("expr", ("NOT internal_err", "NOT (internal_err)", "bogus/")) 1087def test_marker_expr_eval_failure_handling(testdir, expr): 1088 foo = testdir.makepyfile( 1089 """ 1090 import pytest 1091 1092 @pytest.mark.internal_err 1093 def test_foo(): 1094 pass 1095 """ 1096 ) 1097 expected = "ERROR: Wrong expression passed to '-m': {}: *".format(expr) 1098 result = testdir.runpytest(foo, "-m", expr) 1099 result.stderr.fnmatch_lines([expected]) 1100 assert result.ret == ExitCode.USAGE_ERROR 1101