Skip to content

Commit

Permalink
tests: more property based tests, move common fixtures out
Browse files Browse the repository at this point in the history
  • Loading branch information
abhidg committed Sep 1, 2024
1 parent 5dca290 commit fe097ae
Show file tree
Hide file tree
Showing 4 changed files with 53 additions and 13 deletions.
15 changes: 11 additions & 4 deletions sunblock.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,9 +89,11 @@ def process(data: dict[str, Any] | None) -> pd.DataFrame | None:

def find_sun(data: pd.DataFrame, num_hours: int) -> SunblockResult:
"Finds sun for `num_hours` in the data, after `day_start`"
assert (
isinstance(num_hours, int) and 0 < num_hours < 23
), "Number of hours must be between 0 and 23"
assert isinstance(num_hours, int), "num_hours should be an integer"
if num_hours < 1:
raise ValueError("Minimum num_hours is 1")
if num_hours > 23:
raise ValueError("Maximum sun block allowed is 23 hours")
sunny = (
(data.sunrise <= data.time)
& (data.time <= data.sunset)
Expand All @@ -103,7 +105,12 @@ def find_sun(data: pd.DataFrame, num_hours: int) -> SunblockResult:
return SunblockResult(num_hours, None, None, "No sunny interval found")
else:
mean_temp = data.loc[idx : idx + num_hours].temperature_celsius.mean()
return SunblockResult(num_hours, data.loc[idx].time, mean_temp, "")
return SunblockResult(
num_hours,
datetime.datetime.fromisoformat(data.loc[idx].time),
mean_temp,
"",
)


if __name__ == "__main__":
Expand Down
13 changes: 13 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
"Fixtures common to all tests"

import json
import pytest
from pathlib import Path

from sunblock import process


@pytest.fixture(scope="session")
def openmeteo_data():
data = json.loads(Path(__file__).with_name("openmeteo.json").read_text())
return process(data)
15 changes: 7 additions & 8 deletions tests/test_examples.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,10 @@
process,
find_sun,
SunblockResult,
NEWCASTLE
NEWCASTLE,
)


@pytest.fixture(scope="session")
def openmeteo_data():
data = json.loads(Path(__file__).with_name("openmeteo.json").read_text())
return process(data)


def test_openmeteo_url():
assert openmeteo_api_url(Location(1, -1)) == (
"https://api.open-meteo.com/v1/forecast?latitude=1&longitude=-1"
Expand All @@ -40,6 +34,7 @@ def test_fetch_network():
assert_approx_equal(data["longitude"], NEWCASTLE[1])
assert len(data["hourly"]["time"]) == 24 * 7


@pytest.mark.parametrize(
"fahrenheit,expected_celsius",
[
Expand Down Expand Up @@ -67,7 +62,11 @@ def test_process(openmeteo_data):
def test_find_sun(openmeteo_data):
block = find_sun(openmeteo_data, num_hours=3)
assert_approx_equal(block.mean_temp_celsius, 19.916666666666668)
assert (block.num_hours, block.start, block.message) == (3, "2024-08-23T11:00", "")
assert (block.num_hours, block.start, block.message) == (
3,
datetime.datetime.fromisoformat("2024-08-23T11:00"),
"",
)


def test_find_sun_not_found(openmeteo_data):
Expand Down
23 changes: 22 additions & 1 deletion tests/test_property.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,14 @@
Property based testing
Testing module using hypothesis
"""
import json
from pathlib import Path

import pytest
from hypothesis import given, reject, settings
from hypothesis.strategies import floats, integers

from sunblock import fahrenheit_to_celsius
from sunblock import fahrenheit_to_celsius, find_sun

# In property based testing, we encode assumptions or properties of the
# system that should always be true, regardless of parameter values a
Expand Down Expand Up @@ -39,3 +43,20 @@ def test_fahrenheit_differs_from_celsius(temp):
assert fahrenheit_to_celsius(temp) != temp
except ValueError:
reject()


# Test that a range of sunblock lengths does not crash the program
@given(num_hours=integers(min_value=1, max_value=23))
def test_find_sun(num_hours, openmeteo_data):
block = find_sun(openmeteo_data, num_hours)
if block.message != "No sunny interval found":
assert block.num_hours == num_hours
# This is not true everywhere of course, but should be true
# for the sample data that we are using here
# Ideally the sample data would also be autogenerated, perhaps
# using hypothesis-jsonschema and the function tested in a variety
# of geographical locations
assert 4 <= block.start.time().hour < 23

# Extremely unlikely in our test sample to have 4h contiguous sun
assert num_hours < 4

0 comments on commit fe097ae

Please sign in to comment.