or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

cli-migration.mdcore-time-travel.mdescape-hatch.mdindex.mdpytest-integration.md

pytest-integration.mddocs/

0

# Pytest Integration

1

2

Time Machine provides seamless integration with pytest through fixtures, markers, and automatic test discovery. This enables both imperative and declarative approaches to time travel in tests.

3

4

## Capabilities

5

6

### Time Machine Fixture

7

8

A pytest fixture that provides a high-level interface for time travel within test functions. The fixture automatically manages time travel lifecycle and provides methods for time manipulation.

9

10

```python { .api }

11

class TimeMachineFixture:

12

def move_to(self, destination: DestinationType, tick: bool | None = None) -> None:

13

"""

14

Initialize or move to a time destination.

15

16

Parameters:

17

- destination: Target time (timestamp, datetime, string, etc.)

18

- tick: Whether time advances during travel (optional)

19

"""

20

21

def shift(self, delta: dt.timedelta | int | float) -> None:

22

"""

23

Shift time by a relative amount.

24

25

Parameters:

26

- delta: Time delta as timedelta object, or seconds as int/float

27

28

Raises:

29

RuntimeError: If called before move_to()

30

"""

31

32

def stop(self) -> None:

33

"""Stop time travel and restore real time."""

34

```

35

36

```python { .api }

37

@pytest.fixture(name="time_machine")

38

def time_machine_fixture(request: pytest.FixtureRequest) -> Generator[TimeMachineFixture, None, None]:

39

"""

40

Pytest fixture providing TimeMachineFixture instance.

41

42

Automatically processes @pytest.mark.time_machine markers and

43

ensures proper cleanup after test completion.

44

45

Parameters:

46

- request: Pytest fixture request object

47

48

Yields:

49

TimeMachineFixture instance for test use

50

"""

51

```

52

53

Usage examples:

54

55

```python

56

import pytest

57

from datetime import datetime, timedelta

58

59

def test_with_fixture(time_machine):

60

# Start time travel

61

time_machine.move_to("2023-01-01 12:00:00")

62

assert datetime.now().year == 2023

63

assert datetime.now().hour == 12

64

65

# Shift time forward

66

time_machine.shift(3600) # 1 hour

67

assert datetime.now().hour == 13

68

69

# Move to new destination

70

time_machine.move_to("2024-06-15")

71

assert datetime.now().year == 2024

72

assert datetime.now().month == 6

73

74

def test_with_timedelta_shift(time_machine):

75

time_machine.move_to("2023-01-01")

76

77

# Shift using timedelta

78

time_machine.shift(timedelta(days=7, hours=3))

79

assert datetime.now().day == 8

80

assert datetime.now().hour == 3

81

82

def test_tick_modes(time_machine):

83

# Static time mode

84

time_machine.move_to("2023-01-01", tick=False)

85

time1 = datetime.now()

86

time.sleep(0.1)

87

time2 = datetime.now()

88

assert time1 == time2

89

90

# Switch to advancing time

91

time_machine.move_to("2023-01-01", tick=True)

92

time1 = datetime.now()

93

time.sleep(0.1)

94

time2 = datetime.now()

95

assert time2 > time1

96

```

97

98

### Pytest Markers

99

100

Declarative time travel using pytest markers. Tests marked with `@pytest.mark.time_machine` automatically receive time travel without explicit fixture usage.

101

102

```python { .api }

103

def pytest_collection_modifyitems(items: list[pytest.Item]) -> None:

104

"""

105

Pytest hook that automatically adds time_machine fixture to tests

106

with the time_machine marker.

107

108

Parameters:

109

- items: List of collected test items

110

"""

111

112

def pytest_configure(config: pytest.Config) -> None:

113

"""

114

Pytest hook that registers the time_machine marker.

115

116

Parameters:

117

- config: Pytest configuration object

118

"""

119

```

120

121

Usage examples:

122

123

```python

124

import pytest

125

from datetime import datetime

126

127

# Basic marker usage

128

@pytest.mark.time_machine("2023-01-01")

129

def test_new_year():

130

assert datetime.now().year == 2023

131

132

# Marker with datetime object

133

@pytest.mark.time_machine(datetime(2023, 6, 15, 10, 30))

134

def test_specific_datetime():

135

assert datetime.now().month == 6

136

assert datetime.now().day == 15

137

assert datetime.now().hour == 10

138

139

# Marker with tick=False

140

@pytest.mark.time_machine("2023-01-01", tick=False)

141

def test_static_time():

142

time1 = datetime.now()

143

time.sleep(0.1)

144

time2 = datetime.now()

145

assert time1 == time2

146

147

# Marker with timestamp

148

@pytest.mark.time_machine(0) # Unix epoch

149

def test_epoch():

150

assert datetime.now().year == 1970

151

152

# Combining marker with fixture for additional control

153

@pytest.mark.time_machine("2023-01-01")

154

def test_marker_with_fixture(time_machine):

155

assert datetime.now().year == 2023

156

157

# Additional time manipulation

158

time_machine.shift(timedelta(months=6))

159

assert datetime.now().month == 7

160

```

161

162

### Parametrized Time Tests

163

164

Combine pytest parametrization with time travel for comprehensive time-based testing:

165

166

```python

167

@pytest.mark.parametrize("test_date", [

168

"2023-01-01",

169

"2023-06-15",

170

"2023-12-25"

171

])

172

def test_multiple_dates(time_machine, test_date):

173

time_machine.move_to(test_date)

174

parsed_date = datetime.strptime(test_date, "%Y-%m-%d")

175

assert datetime.now().year == parsed_date.year

176

assert datetime.now().month == parsed_date.month

177

assert datetime.now().day == parsed_date.day

178

179

@pytest.mark.parametrize("hours_shift", [1, 6, 12, 24])

180

def test_time_shifts(time_machine, hours_shift):

181

time_machine.move_to("2023-01-01 00:00:00")

182

time_machine.shift(hours_shift * 3600)

183

184

expected_hour = hours_shift % 24

185

assert datetime.now().hour == expected_hour

186

```

187

188

### Test Class Integration

189

190

Time Machine can be applied to entire test classes using markers:

191

192

```python

193

@pytest.mark.time_machine("2023-01-01")

194

class TestNewYearFeatures:

195

def test_feature_one(self):

196

assert datetime.now().year == 2023

197

198

def test_feature_two(self, time_machine):

199

assert datetime.now().year == 2023

200

# Can still manipulate time within tests

201

time_machine.shift(timedelta(days=30))

202

assert datetime.now().month == 2

203

```

204

205

### Async Test Support

206

207

Time Machine works with async tests through the async context manager interface:

208

209

```python

210

@pytest.mark.asyncio

211

async def test_async_time_travel():

212

async with time_machine.travel("2023-01-01"):

213

assert datetime.now().year == 2023

214

await asyncio.sleep(0.1) # Async operations work normally

215

216

@pytest.mark.asyncio

217

@pytest.mark.time_machine("2023-01-01")

218

async def test_async_with_marker():

219

assert datetime.now().year == 2023

220

await some_async_operation()

221

```

222

223

### Configuration

224

225

The pytest integration can be configured through pytest configuration files:

226

227

```ini

228

# pytest.ini

229

[tool:pytest]

230

markers =

231

time_machine: Set time for testing with time-machine

232

```

233

234

```python

235

# conftest.py - Custom fixture configuration

236

import pytest

237

import time_machine

238

239

@pytest.fixture(scope="session")

240

def frozen_time():

241

"""Session-scoped time freeze for consistent test runs."""

242

with time_machine.travel("2023-01-01", tick=False):

243

yield

244

```

245

246

### Error Handling

247

248

The pytest integration provides clear error messages for common mistakes:

249

250

```python

251

def test_shift_before_move(time_machine):

252

# This will raise RuntimeError

253

try:

254

time_machine.shift(3600)

255

except RuntimeError as e:

256

assert "Initialize time_machine with move_to()" in str(e)

257

```

258

259

## Type Definitions

260

261

```python { .api }

262

DestinationType = Union[

263

int, float, dt.datetime, dt.timedelta, dt.date, str,

264

Callable[[], Any], Generator[Any, None, None]

265

]

266

```