Skip to content

Commit 0f672d3

Browse files
tests: clean up pytest configuration and setup (#321)
1 parent b9e68ba commit 0f672d3

6 files changed

Lines changed: 121 additions & 168 deletions

File tree

pyproject.toml

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,12 +36,14 @@ version = {attr = "batcontrol.__pkginfo__.__version__"}
3636
required-version = "~=0.7.0" # Pin uv to major version for stability
3737

3838
# Testing configuration
39-
[tool.pytest]
39+
[tool.pytest.ini_options]
4040
testpaths = ["tests"]
41-
python_files = "test_*.py"
42-
python_classes = "Test*"
43-
python_functions = "test_*"
41+
python_files = ["test_*.py"]
42+
python_classes = ["Test*"]
43+
python_functions = ["test_*"]
4444
pythonpath = ["src"]
45+
addopts = "--strict-markers -v"
46+
asyncio_mode = "auto"
4547

4648
# For testing
4749
[project.optional-dependencies]

pytest.ini

Lines changed: 0 additions & 8 deletions
This file was deleted.

tests/batcontrol/inverter/test_dummy.py

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,4 @@
11
import pytest
2-
import sys
3-
import os
4-
5-
# Add the src directory to Python path for testing
6-
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..', '..', 'src'))
72

83
from batcontrol.inverter.dummy import Dummy
94
from batcontrol.inverter.inverter import Inverter

tests/batcontrol/inverter/test_mqtt_inverter.py

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,6 @@
11
import pytest
2-
import sys
3-
import os
42
from unittest.mock import Mock, MagicMock, patch, call
53

6-
# Add the src directory to Python path for testing
7-
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..', '..', 'src'))
8-
94
from batcontrol.inverter.mqtt_inverter import MqttInverter
105
from batcontrol.inverter.inverter import Inverter
116

tests/batcontrol/test_production_offset.py

Lines changed: 115 additions & 141 deletions
Original file line numberDiff line numberDiff line change
@@ -66,172 +66,146 @@ def mock_config(self):
6666
},
6767
}
6868

69-
def test_production_offset_initialization_default(self, mock_config):
69+
@pytest.fixture
70+
def batcontrol_with_patched_factories(self, mock_config, mocker):
71+
core_module = 'batcontrol.core'
72+
73+
mocker.patch(f'{core_module}.tariff_factory')
74+
mocker.patch(f'{core_module}.inverter_factory')
75+
mocker.patch(f'{core_module}.solar_factory')
76+
mocker.patch(f'{core_module}.consumption_factory')
77+
78+
bc = Batcontrol(mock_config)
79+
80+
yield bc
81+
82+
bc.shutdown()
83+
84+
def test_production_offset_initialization_default(
85+
self, mock_config, batcontrol_with_patched_factories
86+
):
7087
"""Test that production offset initializes with default value when not configured"""
71-
# Remove production_offset_percent from config
7288
del mock_config['battery_control_expert']['production_offset_percent']
7389

74-
with patch('batcontrol.core.tariff_factory'), \
75-
patch('batcontrol.core.inverter_factory'), \
76-
patch('batcontrol.core.solar_factory'), \
77-
patch('batcontrol.core.consumption_factory'):
90+
batcontrol = Batcontrol(mock_config)
7891

79-
batcontrol = Batcontrol(mock_config)
92+
assert batcontrol.production_offset_percent == 1.0
93+
batcontrol.shutdown()
8094

81-
# Should default to 1.0 (100%, no offset)
82-
assert batcontrol.production_offset_percent == 1.0
83-
84-
def test_production_offset_initialization_from_config(self, mock_config):
95+
def test_production_offset_initialization_from_config(
96+
self, batcontrol_with_patched_factories
97+
):
8598
"""Test that production offset is initialized from config"""
86-
with patch('batcontrol.core.tariff_factory'), \
87-
patch('batcontrol.core.inverter_factory'), \
88-
patch('batcontrol.core.solar_factory'), \
89-
patch('batcontrol.core.consumption_factory'):
90-
91-
batcontrol = Batcontrol(mock_config)
99+
batcontrol = batcontrol_with_patched_factories
92100

93-
# Should load value from config
94-
assert batcontrol.production_offset_percent == 0.8
101+
assert batcontrol.production_offset_percent == 0.8
95102

96-
def test_production_offset_applied_to_forecast(self, mock_config):
103+
def test_production_offset_applied_to_forecast(
104+
self, batcontrol_with_patched_factories, mocker
105+
):
97106
"""Test that production offset is applied to production forecast"""
98-
with patch('batcontrol.core.tariff_factory'), \
99-
patch('batcontrol.core.inverter_factory'), \
100-
patch('batcontrol.core.solar_factory'), \
101-
patch('batcontrol.core.consumption_factory'):
102-
103-
batcontrol = Batcontrol(mock_config)
104-
batcontrol.production_offset_percent = 0.5 # 50% reduction
105-
106-
# Create mock forecasts
107-
production_forecast = {0: 1000, 1: 2000, 2: 3000} # W
108-
consumption_forecast = {0: 500, 1: 500, 2: 500}
109-
price_dict = {0: 0.20, 1: 0.25, 2: 0.30}
110-
111-
# Mock the forecast methods
112-
batcontrol.dynamic_tariff = Mock()
113-
batcontrol.dynamic_tariff.get_prices = Mock(return_value=price_dict)
114-
115-
batcontrol.fc_solar = Mock()
116-
batcontrol.fc_solar.get_forecast = Mock(return_value=production_forecast)
117-
118-
batcontrol.fc_consumption = Mock()
119-
batcontrol.fc_consumption.get_forecast = Mock(return_value=consumption_forecast)
120-
121-
batcontrol.inverter = Mock()
122-
batcontrol.inverter.get_SOC = Mock(return_value=50.0)
123-
batcontrol.inverter.get_stored_energy = Mock(return_value=5000)
124-
batcontrol.inverter.get_stored_usable_energy = Mock(return_value=4000)
125-
batcontrol.inverter.get_free_capacity = Mock(return_value=5000)
126-
batcontrol.inverter.get_max_capacity = Mock(return_value=10000)
127-
batcontrol.inverter.get_reserved_energy = Mock(return_value=1000)
128-
129-
batcontrol.mqtt_api = None
130-
batcontrol.evcc_api = None
131-
132-
# Mock LogicFactory to avoid complex logic
133-
with patch('batcontrol.core.LogicFactory') as mock_logic_factory:
134-
mock_logic = Mock()
135-
mock_logic.mode = 10
136-
mock_logic.charge_rate = 0
137-
138-
# Create proper InverterControlSettings object
139-
inverter_settings = InverterControlSettings(
140-
allow_discharge=True,
141-
charge_from_grid=False,
142-
charge_rate=0,
143-
limit_battery_charge_rate=-1 # No limit
144-
)
145-
mock_logic.get_inverter_control_settings = Mock(return_value=inverter_settings)
146-
147-
# Create proper CalculationOutput object
148-
calc_output = CalculationOutput(
149-
reserved_energy=1000,
150-
required_recharge_energy=0,
151-
min_dynamic_price_difference=0.05
152-
)
153-
mock_logic.get_calculation_output = Mock(return_value=calc_output)
154-
mock_logic.calculate = Mock(return_value=True)
155-
mock_logic.set_calculation_parameters = Mock()
156-
157-
mock_logic_factory.create_logic = Mock(return_value=mock_logic)
158-
159-
# Call run to apply the offset
160-
batcontrol.run()
161-
162-
# Check that production was offset correctly
163-
# Note: production[0] is adjusted for elapsed time in current interval
164-
# so we only check indices [1] and [2] for exact values
165-
assert batcontrol.last_production[1] == pytest.approx(1000.0)
166-
assert batcontrol.last_production[2] == pytest.approx(1500.0)
167-
168-
def test_production_offset_api_set_valid(self, mock_config):
107+
batcontrol = batcontrol_with_patched_factories
108+
batcontrol.production_offset_percent = 0.5 # 50% reduction
109+
110+
production_forecast = {0: 1000, 1: 2000, 2: 3000} # W
111+
consumption_forecast = {0: 500, 1: 500, 2: 500}
112+
price_dict = {0: 0.20, 1: 0.25, 2: 0.30}
113+
114+
batcontrol.dynamic_tariff = mocker.Mock()
115+
batcontrol.dynamic_tariff.get_prices = mocker.Mock(return_value=price_dict)
116+
117+
batcontrol.fc_solar = mocker.Mock()
118+
batcontrol.fc_solar.get_forecast = mocker.Mock(return_value=production_forecast)
119+
120+
batcontrol.fc_consumption = mocker.Mock()
121+
batcontrol.fc_consumption.get_forecast = mocker.Mock(return_value=consumption_forecast)
122+
123+
batcontrol.inverter = mocker.Mock()
124+
batcontrol.inverter.get_SOC = mocker.Mock(return_value=50.0)
125+
batcontrol.inverter.get_stored_energy = mocker.Mock(return_value=5000)
126+
batcontrol.inverter.get_stored_usable_energy = mocker.Mock(return_value=4000)
127+
batcontrol.inverter.get_free_capacity = mocker.Mock(return_value=5000)
128+
batcontrol.inverter.get_max_capacity = mocker.Mock(return_value=10000)
129+
batcontrol.inverter.get_reserved_energy = mocker.Mock(return_value=1000)
130+
131+
batcontrol.mqtt_api = None
132+
batcontrol.evcc_api = None
133+
134+
mock_logic_factory = mocker.patch('batcontrol.core.LogicFactory')
135+
mock_logic = mocker.Mock()
136+
mock_logic.mode = 10
137+
mock_logic.charge_rate = 0
138+
139+
inverter_settings = InverterControlSettings(
140+
allow_discharge=True,
141+
charge_from_grid=False,
142+
charge_rate=0,
143+
limit_battery_charge_rate=-1,
144+
)
145+
mock_logic.get_inverter_control_settings = mocker.Mock(
146+
return_value=inverter_settings
147+
)
148+
149+
calc_output = CalculationOutput(
150+
reserved_energy=1000,
151+
required_recharge_energy=0,
152+
min_dynamic_price_difference=0.05,
153+
)
154+
mock_logic.get_calculation_output = mocker.Mock(return_value=calc_output)
155+
mock_logic.calculate = mocker.Mock(return_value=True)
156+
mock_logic.set_calculation_parameters = mocker.Mock()
157+
158+
mock_logic_factory.create_logic = mocker.Mock(return_value=mock_logic)
159+
160+
batcontrol.run()
161+
162+
assert batcontrol.last_production[1] == pytest.approx(1000.0)
163+
assert batcontrol.last_production[2] == pytest.approx(1500.0)
164+
165+
def test_production_offset_api_set_valid(self, batcontrol_with_patched_factories):
169166
"""Test setting production offset via API with a valid mid-range value"""
170-
with patch('batcontrol.core.tariff_factory'), \
171-
patch('batcontrol.core.inverter_factory'), \
172-
patch('batcontrol.core.solar_factory'), \
173-
patch('batcontrol.core.consumption_factory'):
174-
175-
batcontrol = Batcontrol(mock_config)
167+
batcontrol = batcontrol_with_patched_factories
176168

177-
# Set a typical valid value (70% of production)
178-
batcontrol.api_set_production_offset(0.7)
169+
batcontrol.api_set_production_offset(0.7)
179170

180-
# Should be updated
181-
assert batcontrol.production_offset_percent == pytest.approx(0.7)
171+
assert batcontrol.production_offset_percent == pytest.approx(0.7)
182172

183-
def test_production_offset_api_set_invalid_negative(self, mock_config):
173+
def test_production_offset_api_set_invalid_negative(
174+
self, batcontrol_with_patched_factories
175+
):
184176
"""Test setting production offset via API with an invalid negative value"""
185-
with patch('batcontrol.core.tariff_factory'), \
186-
patch('batcontrol.core.inverter_factory'), \
187-
patch('batcontrol.core.solar_factory'), \
188-
patch('batcontrol.core.consumption_factory'):
177+
batcontrol = batcontrol_with_patched_factories
178+
original_value = batcontrol.production_offset_percent
189179

190-
batcontrol = Batcontrol(mock_config)
191-
original_value = batcontrol.production_offset_percent
180+
batcontrol.api_set_production_offset(-0.5)
192181

193-
# Try to set invalid negative value
194-
batcontrol.api_set_production_offset(-0.5)
182+
assert batcontrol.production_offset_percent == original_value
195183

196-
# Should not be updated
197-
assert batcontrol.production_offset_percent == original_value
198-
199-
def test_production_offset_api_set_invalid_too_high(self, mock_config):
184+
def test_production_offset_api_set_invalid_too_high(
185+
self, batcontrol_with_patched_factories
186+
):
200187
"""Test setting production offset via API with invalid high value"""
201-
with patch('batcontrol.core.tariff_factory'), \
202-
patch('batcontrol.core.inverter_factory'), \
203-
patch('batcontrol.core.solar_factory'), \
204-
patch('batcontrol.core.consumption_factory'):
205-
206-
batcontrol = Batcontrol(mock_config)
207-
original_value = batcontrol.production_offset_percent
188+
batcontrol = batcontrol_with_patched_factories
189+
original_value = batcontrol.production_offset_percent
208190

209-
# Try to set invalid value (> 2.0)
210-
batcontrol.api_set_production_offset(2.5)
191+
batcontrol.api_set_production_offset(2.5)
211192

212-
# Should not be updated
213-
assert batcontrol.production_offset_percent == original_value
193+
assert batcontrol.production_offset_percent == original_value
214194

215-
def test_production_offset_api_set_boundary_values(self, mock_config):
195+
def test_production_offset_api_set_boundary_values(
196+
self, batcontrol_with_patched_factories
197+
):
216198
"""Test setting production offset via API with boundary values"""
217-
with patch('batcontrol.core.tariff_factory'), \
218-
patch('batcontrol.core.inverter_factory'), \
219-
patch('batcontrol.core.solar_factory'), \
220-
patch('batcontrol.core.consumption_factory'):
221-
222-
batcontrol = Batcontrol(mock_config)
199+
batcontrol = batcontrol_with_patched_factories
223200

224-
# Test minimum boundary (0.0)
225-
batcontrol.api_set_production_offset(0.0)
226-
assert batcontrol.production_offset_percent == 0.0
201+
batcontrol.api_set_production_offset(0.0)
202+
assert batcontrol.production_offset_percent == 0.0
227203

228-
# Test maximum boundary (2.0)
229-
batcontrol.api_set_production_offset(2.0)
230-
assert batcontrol.production_offset_percent == 2.0
204+
batcontrol.api_set_production_offset(2.0)
205+
assert batcontrol.production_offset_percent == 2.0
231206

232-
# Test normal value (1.0 = 100%)
233-
batcontrol.api_set_production_offset(1.0)
234-
assert batcontrol.production_offset_percent == 1.0
207+
batcontrol.api_set_production_offset(1.0)
208+
assert batcontrol.production_offset_percent == 1.0
235209

236210

237211
class TestProductionOffsetMqtt:

tests/conftest.py

Lines changed: 0 additions & 5 deletions
This file was deleted.

0 commit comments

Comments
 (0)