@@ -9,9 +9,12 @@ This document provides guidelines for AI agents working on the Jazzband.co codeb
99This project uses ** pytest** with the following conventions:
1010
1111- ✅ ** Use pytest-mock's ` mocker ` fixture** - Available automatically via ` pytest-mock ` plugin
12+ - ✅ ** Use ` mocker.patch() ` for patching** - Automatically cleaned up after each test
13+ - ✅ ** Use ` monkeypatch ` fixture for simple attribute/env changes** - When full mocking isn't needed
1214- ✅ ** Function-based tests** - Not class-based test methods
1315- ❌ ** Never import ` unittest.mock ` or ` mock ` ** - Use ` mocker ` fixture instead
14- - ❌ ** No ` @patch ` decorators** - Use ` patch() ` as context managers instead
16+ - ❌ ** No ` @patch ` decorators** - Use ` mocker.patch() ` instead
17+ - ❌ ** No ` with patch() ` context managers** - Use ` mocker.patch() ` instead
1518
1619### Test Structure
1720
@@ -21,10 +24,10 @@ def test_function_name(fixture1, fixture2, mocker):
2124 # Arrange - Set up test data and mocks
2225 mock_obj = mocker.MagicMock()
2326 mock_obj.method.return_value = expected_value
27+ mock_something = mocker.patch(" module.path.to.object" )
2428
2529 # Act - Call the function under test
26- with patch(" module.path.to.object" ) as mock_something:
27- result = function_under_test(args)
30+ result = function_under_test(args)
2831
2932 # Assert - Verify the results
3033 assert result == expected_value
@@ -62,34 +65,35 @@ def test_with_database_query(mocker):
6265 mock_user = mocker.MagicMock()
6366 mock_user.login = " test-user"
6467
65- with patch(" module.path.User" ) as mock_user_class:
66- mock_user_class.query.get.return_value = mock_user
68+ mock_user_class = mocker. patch(" module.path.User" )
69+ mock_user_class.query.get.return_value = mock_user
6770
68- # Your test code here
69- result = function_that_queries_user(user_id = 123 )
71+ # Your test code here
72+ result = function_that_queries_user(user_id = 123 )
7073
71- # Verify the query was called
72- mock_user_class.query.get.assert_called_once_with(123 )
74+ # Verify the query was called
75+ mock_user_class.query.get.assert_called_once_with(123 )
7376```
7477
7578#### Mocking GitHub API Calls
7679
7780``` python
7881def test_github_api_call (mocker , test_app_context ):
7982 """ Test function that calls GitHub API."""
80- with patch(" jazzband.projects.tasks.github" ) as mock_github:
81- # Setup mock response
82- mock_response = mocker.MagicMock()
83- mock_response.status_code = 200
84- mock_response.json.return_value = {" success" : True }
85- mock_github.some_method.return_value = mock_response
86-
87- # Call function
88- result = function_that_uses_github_api()
89-
90- # Verify
91- mock_github.some_method.assert_called_once()
92- assert result is not None
83+ mock_github = mocker.patch(" jazzband.projects.tasks.github" )
84+
85+ # Setup mock response
86+ mock_response = mocker.MagicMock()
87+ mock_response.status_code = 200
88+ mock_response.json.return_value = {" success" : True }
89+ mock_github.some_method.return_value = mock_response
90+
91+ # Call function
92+ result = function_that_uses_github_api()
93+
94+ # Verify
95+ mock_github.some_method.assert_called_once()
96+ assert result is not None
9397```
9498
9599#### Mocking Multiple Related Objects
@@ -105,13 +109,13 @@ def test_with_multiple_mocks(mocker):
105109 mock_project.team_slug = " test-project"
106110 mock_project.leads_team_slug = " test-project-leads"
107111
108- # Patch multiple classes
109- with patch(" module.User" ) as mock_user_class:
110- with patch(" module.Project" ) as mock_project_class:
111- mock_user_class.query.get.return_value = mock_user
112- mock_project_class.query.get.return_value = mock_project
112+ # Patch multiple classes using mocker.patch
113+ mock_user_class = mocker. patch(" module.User" )
114+ mock_project_class = mocker. patch(" module.Project" )
115+ mock_user_class.query.get.return_value = mock_user
116+ mock_project_class.query.get.return_value = mock_project
113117
114- # Test code here
118+ # Test code here
115119```
116120
117121### Testing GitHub API Methods
@@ -157,23 +161,24 @@ def test_task_function(mocker, test_app_context):
157161 mock_project = mocker.MagicMock()
158162 mock_project.name = " test-project"
159163
160- # Patch database and GitHub
161- with patch(" jazzband.projects.tasks.User" ) as mock_user_class:
162- with patch(" jazzband.projects.tasks.Project" ) as mock_project_class:
163- with patch(" jazzband.projects.tasks.github" ) as mock_github:
164- # Setup returns
165- mock_user_class.query.get.return_value = mock_user
166- mock_project_class.query.get.return_value = mock_project
164+ # Patch database and GitHub using mocker.patch
165+ mock_user_class = mocker.patch(" jazzband.projects.tasks.User" )
166+ mock_project_class = mocker.patch(" jazzband.projects.tasks.Project" )
167+ mock_github = mocker.patch(" jazzband.projects.tasks.github" )
167168
168- mock_response = mocker.MagicMock()
169- mock_response.status_code = 200
170- mock_github.some_api_call. return_value = mock_response
169+ # Setup returns
170+ mock_user_class.query.get.return_value = mock_user
171+ mock_project_class.query.get. return_value = mock_project
171172
172- # Call the task
173- task_function(user_id, project_id)
173+ mock_response = mocker.MagicMock()
174+ mock_response.status_code = 200
175+ mock_github.some_api_call.return_value = mock_response
176+
177+ # Call the task
178+ task_function(user_id, project_id)
174179
175- # Verify interactions
176- mock_github.some_api_call.assert_called_once()
180+ # Verify interactions
181+ mock_github.some_api_call.assert_called_once()
177182```
178183
179184### Assertion Patterns
@@ -263,26 +268,27 @@ def test_add_user_to_team_lead_member(mocker, test_app_context):
263268 mock_project.team_slug = " test-project"
264269 mock_project.leads_team_slug = " test-project-leads"
265270
266- # Mock User.query.get and Project.query.get
267- with patch(" jazzband.projects.tasks.User" ) as mock_user_class:
268- with patch(" jazzband.projects.tasks.Project" ) as mock_project_class:
269- with patch(" jazzband.projects.tasks.github" ) as mock_github:
270- mock_user_class.query.get.return_value = mock_user
271- mock_project_class.query.get.return_value = mock_project
272-
273- # Mock successful responses
274- mock_response = mocker.MagicMock()
275- mock_response.status_code = 200
276- mock_github.join_team.return_value = mock_response
277-
278- # Call the task
279- add_user_to_team(user_id, project_id, is_lead)
280-
281- # Verify join_team was called twice (main team and leads team)
282- assert mock_github.join_team.call_count == 2
283- calls = mock_github.join_team.call_args_list
284- assert calls[0 ][0 ] == (" test-project" , " test-lead" )
285- assert calls[1 ][0 ] == (" test-project-leads" , " test-lead" )
271+ # Mock User.query.get and Project.query.get using mocker.patch
272+ mock_user_class = mocker.patch(" jazzband.projects.tasks.User" )
273+ mock_project_class = mocker.patch(" jazzband.projects.tasks.Project" )
274+ mock_github = mocker.patch(" jazzband.projects.tasks.github" )
275+
276+ mock_user_class.query.get.return_value = mock_user
277+ mock_project_class.query.get.return_value = mock_project
278+
279+ # Mock successful responses
280+ mock_response = mocker.MagicMock()
281+ mock_response.status_code = 200
282+ mock_github.join_team.return_value = mock_response
283+
284+ # Call the task
285+ add_user_to_team(user_id, project_id, is_lead)
286+
287+ # Verify join_team was called twice (main team and leads team)
288+ assert mock_github.join_team.call_count == 2
289+ calls = mock_github.join_team.call_args_list
290+ assert calls[0 ][0 ] == (" test-project" , " test-lead" )
291+ assert calls[1 ][0 ] == (" test-project-leads" , " test-lead" )
286292```
287293
288294## Code Style
@@ -476,6 +482,11 @@ from unittest.mock import Mock, patch
476482def test_function (mock_something ):
477483 pass
478484
485+ # Using with patch() context managers
486+ def test_function ():
487+ with patch(" module.something" ) as mock_something:
488+ pass
489+
479490# Class-based tests
480491class TestSomething :
481492 def test_method (self ):
@@ -485,14 +496,18 @@ class TestSomething:
485496### ✅ Do This Instead
486497
487498``` python
488- # Use mocker fixture
499+ # Use mocker fixture for mocks
489500def test_function (mocker ):
490501 mock_obj = mocker.MagicMock()
491502
492- # Use patch as context manager
503+ # Use mocker. patch() for patching
493504def test_function (mocker ):
494- with patch(" module.something" ) as mock_something:
495- pass
505+ mock_something = mocker.patch(" module.something" )
506+
507+ # Use monkeypatch for simple attribute changes
508+ def test_function (monkeypatch ):
509+ monkeypatch.setattr(" module.CONSTANT" , " new_value" )
510+ monkeypatch.setenv(" ENV_VAR" , " test_value" )
496511
497512# Function-based tests
498513def test_something (mocker ):
0 commit comments