Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1import pytest 

2 

3 

4def pytest_addoption(parser): 

5 group = parser.getgroup("general") 

6 group.addoption( 

7 "--sw", 

8 "--stepwise", 

9 action="store_true", 

10 dest="stepwise", 

11 help="exit on test failure and continue from last failing test next time", 

12 ) 

13 group.addoption( 

14 "--stepwise-skip", 

15 action="store_true", 

16 dest="stepwise_skip", 

17 help="ignore the first failing test but stop on the next failing test", 

18 ) 

19 

20 

21@pytest.hookimpl 

22def pytest_configure(config): 

23 config.pluginmanager.register(StepwisePlugin(config), "stepwiseplugin") 

24 

25 

26class StepwisePlugin: 

27 def __init__(self, config): 

28 self.config = config 

29 self.active = config.getvalue("stepwise") 

30 self.session = None 

31 self.report_status = "" 

32 

33 if self.active: 

34 self.lastfailed = config.cache.get("cache/stepwise", None) 

35 self.skip = config.getvalue("stepwise_skip") 

36 

37 def pytest_sessionstart(self, session): 

38 self.session = session 

39 

40 def pytest_collection_modifyitems(self, session, config, items): 

41 if not self.active: 

42 return 

43 if not self.lastfailed: 

44 self.report_status = "no previously failed tests, not skipping." 

45 return 

46 

47 already_passed = [] 

48 found = False 

49 

50 # Make a list of all tests that have been run before the last failing one. 

51 for item in items: 

52 if item.nodeid == self.lastfailed: 

53 found = True 

54 break 

55 else: 

56 already_passed.append(item) 

57 

58 # If the previously failed test was not found among the test items, 

59 # do not skip any tests. 

60 if not found: 

61 self.report_status = "previously failed test not found, not skipping." 

62 already_passed = [] 

63 else: 

64 self.report_status = "skipping {} already passed items.".format( 

65 len(already_passed) 

66 ) 

67 

68 for item in already_passed: 

69 items.remove(item) 

70 

71 config.hook.pytest_deselected(items=already_passed) 

72 

73 def pytest_runtest_logreport(self, report): 

74 if not self.active: 

75 return 

76 

77 if report.failed: 

78 if self.skip: 

79 # Remove test from the failed ones (if it exists) and unset the skip option 

80 # to make sure the following tests will not be skipped. 

81 if report.nodeid == self.lastfailed: 

82 self.lastfailed = None 

83 

84 self.skip = False 

85 else: 

86 # Mark test as the last failing and interrupt the test session. 

87 self.lastfailed = report.nodeid 

88 self.session.shouldstop = ( 

89 "Test failed, continuing from this test next run." 

90 ) 

91 

92 else: 

93 # If the test was actually run and did pass. 

94 if report.when == "call": 

95 # Remove test from the failed ones, if exists. 

96 if report.nodeid == self.lastfailed: 

97 self.lastfailed = None 

98 

99 def pytest_report_collectionfinish(self): 

100 if self.active and self.config.getoption("verbose") >= 0 and self.report_status: 

101 return "stepwise: %s" % self.report_status 

102 

103 def pytest_sessionfinish(self, session): 

104 if self.active: 

105 self.config.cache.set("cache/stepwise", self.lastfailed) 

106 else: 

107 # Clear the list of failing tests if the plugin is not active. 

108 self.config.cache.set("cache/stepwise", [])