Skip to content

Commit 042dc90

Browse files
committed
Merge branch '2025-testtips'
2 parents 9923e91 + 65c1ce6 commit 042dc90

9 files changed

Lines changed: 114 additions & 217 deletions

2025/testtips/tests/test_tips.py

Lines changed: 28 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,73 +1,62 @@
1-
# tests/test_weather.py
2-
31
import sys
2+
from typing import Any
43

54
import pytest
6-
from weather import WeatherService
75

8-
# from hypothesis import given
9-
# from hypothesis.strategies import floats
6+
from weather import WeatherService
107

118

12-
# ✅ Parametrization
139
@pytest.mark.parametrize(
14-
"city,expected_temp", [("London", 15), ("Berlin", 20), ("Paris", 17)]
10+
"city,expected_temp",
11+
[
12+
("London", 15),
13+
("Berlin", 20),
14+
("Rome", 18),
15+
],
1516
)
16-
def test_get_temperature_multiple_cities(monkeypatch, city, expected_temp):
17-
def fake_get(url, params):
17+
def test_parametrized_temperatures(
18+
monkeypatch: pytest.MonkeyPatch, city: str, expected_temp: float
19+
) -> None:
20+
def fake_get(url: str, params: dict[str, Any]) -> Any:
1821
class FakeResponse:
19-
def raise_for_status(self):
22+
def raise_for_status(self) -> None:
2023
pass
2124

22-
def json(self):
25+
def json(self) -> dict[str, Any]:
2326
return {"current": {"temp_c": expected_temp}}
2427

2528
return FakeResponse()
2629

27-
monkeypatch.setattr("weather.httpx.get", fake_get)
28-
30+
monkeypatch.setattr("httpx.get", fake_get)
2931
service = WeatherService(api_key="fake-key")
30-
temp = service.get_temperature(city)
32+
assert service.get_temperature(city) == expected_temp
3133

32-
assert temp == expected_temp
3334

34-
35-
# ✅ pytest.raises
36-
def test_get_temperature_raises_http_error(monkeypatch):
37-
def fake_get(url, params):
35+
def test_temperature_raises_error(monkeypatch: pytest.MonkeyPatch) -> None:
36+
def fake_get(url: str, params: dict[str, Any]) -> Any:
3837
class FakeResponse:
39-
def raise_for_status(self):
38+
def raise_for_status(self) -> None:
4039
raise Exception("API error")
4140

4241
return FakeResponse()
4342

44-
monkeypatch.setattr("weather.httpx.get", fake_get)
45-
43+
monkeypatch.setattr("httpx.get", fake_get)
4644
service = WeatherService(api_key="fake-key")
4745

4846
with pytest.raises(Exception):
49-
service.get_temperature("Tokyo")
47+
service.get_temperature("Oslo")
5048

5149

52-
# ✅ pytest.mark.skip
53-
@pytest.mark.skip(reason="Skipping this test for demonstration purposes.")
54-
def test_skipped_example():
50+
@pytest.mark.skip(reason="Temporarily skipping for demo purposes")
51+
def test_skipped() -> None:
5552
assert False
5653

5754

58-
# ✅ pytest.mark.skipif
59-
@pytest.mark.skipif(sys.platform == "win32", reason="Does not run on Windows")
60-
def test_only_runs_on_non_windows():
55+
@pytest.mark.skipif(sys.platform == "win32", reason="Fails on Windows")
56+
def test_non_windows_behavior() -> None:
6157
assert True
6258

6359

64-
# ✅ pytest.mark.xfail
65-
@pytest.mark.xfail(reason="Known bug: API sometimes returns wrong temperature")
66-
def test_expected_failure_example():
67-
assert 2 + 2 == 5
68-
69-
70-
# ✅ Hypothesis property-based test
71-
# @given(floats(min_value=-50, max_value=50))
72-
# def test_temperature_range_property(temp):
73-
# assert -50 <= temp <= 50
60+
@pytest.mark.xfail(reason="Intentional failure due to API bug")
61+
def test_expected_failure() -> None:
62+
assert 1 + 1 == 3

2025/testtips/tests/test_weather.py

Lines changed: 0 additions & 78 deletions
This file was deleted.
Lines changed: 12 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,19 @@
11
import pytest
22
from weather import WeatherService
3+
from typing import Any
4+
from unittest.mock import MagicMock
35

4-
5-
# This fixture creates a WeatherService instance you can reuse
66
@pytest.fixture
7-
def weather_service():
7+
def weather_service(monkeypatch: pytest.MonkeyPatch) -> WeatherService:
8+
def fake_get(url: str, params: dict[str, Any]) -> Any:
9+
mock_response = MagicMock()
10+
mock_response.raise_for_status.return_value = None
11+
mock_response.json.return_value = {"current": {"temp_c": 25}}
12+
return mock_response
13+
14+
monkeypatch.setattr("httpx.get", fake_get)
815
return WeatherService(api_key="fake-key")
916

17+
def test_fixture_usage(weather_service: WeatherService) -> None:
18+
assert weather_service.get_temperature("Paris") == 25
1019

11-
def test_get_temperature_returns_expected_value(weather_service, monkeypatch):
12-
"""
13-
Test that get_temperature returns the correct temperature.
14-
"""
15-
16-
def fake_get(url, params):
17-
class FakeResponse:
18-
def raise_for_status(self):
19-
pass
20-
21-
def json(self):
22-
return {"current": {"temp_c": 20}}
23-
24-
return FakeResponse()
25-
26-
monkeypatch.setattr("weather.httpx.get", fake_get)
27-
28-
temp = weather_service.get_temperature("Amsterdam")
29-
assert temp == 20
30-
31-
32-
def test_get_temperature_returns_float(weather_service, monkeypatch):
33-
"""
34-
Test that get_temperature returns a float value.
35-
"""
36-
37-
def fake_get(url, params):
38-
class FakeResponse:
39-
def raise_for_status(self):
40-
pass
41-
42-
def json(self):
43-
return {"current": {"temp_c": 18.5}}
44-
45-
return FakeResponse()
46-
47-
monkeypatch.setattr("weather.httpx.get", fake_get)
48-
49-
temp = weather_service.get_temperature("Berlin")
50-
assert isinstance(temp, float)
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
from typing import Any
2+
from unittest.mock import MagicMock, patch
3+
4+
import pytest
5+
6+
from weather import WeatherService
7+
8+
9+
def test_get_temperature_with_mocking_monkeypatch(
10+
monkeypatch: pytest.MonkeyPatch,
11+
) -> None:
12+
def fake_get(url: str, params: dict[str, Any]) -> Any:
13+
mock_response = MagicMock()
14+
mock_response.raise_for_status.return_value = None
15+
mock_response.json.return_value = {"current": {"temp_c": 25}}
16+
return mock_response
17+
18+
monkeypatch.setattr("httpx.get", fake_get)
19+
service = WeatherService(api_key="fake-key")
20+
temp = service.get_temperature("Amsterdam")
21+
assert temp == 25
22+
23+
24+
def test_get_temperature_with_mocking() -> None:
25+
mock_response = MagicMock()
26+
mock_response.raise_for_status.return_value = None
27+
mock_response.json.return_value = {"current": {"temp_c": 25}}
28+
29+
with patch("httpx.get", return_value=mock_response) as mock_get:
30+
service = WeatherService(api_key="fake-key")
31+
temp = service.get_temperature("London")
32+
33+
assert temp == 25
34+
mock_get.assert_called_once()

2025/testtips/tests/test_weather_mock_mp.py

Lines changed: 0 additions & 41 deletions
This file was deleted.
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
from weather import WeatherService
2+
import pytest
3+
from typing import Any
4+
5+
def test_get_temperature_with_monkeypatch(monkeypatch: pytest.MonkeyPatch) -> None:
6+
def fake_get(url: str, params: dict[str, Any]) -> Any:
7+
class FakeResponse:
8+
def raise_for_status(self) -> None: pass
9+
def json(self) -> dict[str, Any]:
10+
return {"current": {"temp_c": 19}}
11+
return FakeResponse()
12+
13+
monkeypatch.setattr("httpx.get", fake_get)
14+
service = WeatherService(api_key="fake-key")
15+
temp = service.get_temperature("Amsterdam")
16+
assert temp == 19
Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,22 @@
1+
from unittest.mock import MagicMock
2+
3+
import pytest
4+
15
from weather_refactor import WeatherService
26

3-
def test_get_temperature_with_stub_client():
4-
class StubClient:
5-
def get(self, url, params):
6-
class Response:
7-
def raise_for_status(self): pass
8-
def json(self): return {"current": {"temp_c": 18}}
9-
return Response()
107

11-
service = WeatherService(client=StubClient(), api_key="fake_key")
12-
assert service.get_temperature("Oslo") == 18
8+
@pytest.fixture
9+
def weather_service() -> WeatherService:
10+
mock_http_client = MagicMock()
11+
mock_http_client.get.return_value = MagicMock(
12+
**{
13+
"raise_for_status": lambda: None,
14+
"json": lambda: {"current": {"temp_c": 17}},
15+
}
16+
)
17+
return WeatherService(client=mock_http_client, api_key="fake-key")
18+
19+
20+
def test_weather_service_with_mock_http_client(weather_service: WeatherService):
21+
temp = weather_service.get_temperature("Amsterdam")
22+
assert temp == 17

2025/testtips/weather.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
# weather_service.py
21
import os
32

43
import httpx
@@ -8,7 +7,7 @@
87

98

109
class WeatherService:
11-
def __init__(self, api_key: str):
10+
def __init__(self, api_key: str) -> None:
1211
self.api_key = api_key
1312

1413
def get_temperature(self, city: str) -> float:
@@ -21,8 +20,8 @@ def get_temperature(self, city: str) -> float:
2120
return data["current"]["temp_c"]
2221

2322

24-
def main():
25-
api_key = os.getenv("WEATHER_API_KEY")
23+
def main() -> None:
24+
api_key = os.getenv("WEATHER_API_KEY", "")
2625
weather_service = WeatherService(api_key)
2726
temperature = weather_service.get_temperature("Amsterdam")
2827
print(f"The current temperature in Amsterdam is {temperature}°C.")

0 commit comments

Comments
 (0)