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

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

import pytest 

 

 

def pytest_addoption(parser): 

group = parser.getgroup("general") 

group.addoption( 

"--sw", 

"--stepwise", 

action="store_true", 

dest="stepwise", 

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

) 

group.addoption( 

"--stepwise-skip", 

action="store_true", 

dest="stepwise_skip", 

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

) 

 

 

@pytest.hookimpl 

def pytest_configure(config): 

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

 

 

class StepwisePlugin: 

def __init__(self, config): 

self.config = config 

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

self.session = None 

 

if self.active: 

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

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

 

def pytest_sessionstart(self, session): 

self.session = session 

 

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

if not self.active or not self.lastfailed: 

return 

 

already_passed = [] 

found = False 

 

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

for item in items: 

if item.nodeid == self.lastfailed: 

found = True 

break 

else: 

already_passed.append(item) 

 

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

# do not skip any tests. 

if not found: 

already_passed = [] 

 

for item in already_passed: 

items.remove(item) 

 

config.hook.pytest_deselected(items=already_passed) 

 

def pytest_collectreport(self, report): 

if self.active and report.failed: 

self.session.shouldstop = ( 

"Error when collecting test, stopping test execution." 

) 

 

def pytest_runtest_logreport(self, report): 

# Skip this hook if plugin is not active or the test is xfailed. 

if not self.active or "xfail" in report.keywords: 

return 

 

if report.failed: 

if self.skip: 

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

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

if report.nodeid == self.lastfailed: 

self.lastfailed = None 

 

self.skip = False 

else: 

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

self.lastfailed = report.nodeid 

self.session.shouldstop = ( 

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

) 

 

else: 

# If the test was actually run and did pass. 

if report.when == "call": 

# Remove test from the failed ones, if exists. 

if report.nodeid == self.lastfailed: 

self.lastfailed = None 

 

def pytest_sessionfinish(self, session): 

if self.active: 

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

else: 

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

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