-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add tests for zone state values (#42)
- Loading branch information
1 parent
bcb1d5f
commit 3870574
Showing
3 changed files
with
328 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
### Unit tests | ||
|
||
Unit tests are written using the standard Python unittest module. | ||
|
||
Tests are written using the Arrange/Act/Assert pattern, where the test data is first set up, | ||
then the actual call to the code is made, and finally the assertion is tested. | ||
|
||
For example: | ||
|
||
``` | ||
def test_when_bIsActive_is_true_then_state_bIsActive_true(self): | ||
"Check that the bIsActive is correctly set to true" | ||
self.raw_json["bIsActive"] = 1 | ||
genius_zone = GeniusZone(self._device_id, self.raw_json, self.hub) | ||
self.assertTrue(genius_zone.data["_state"]["bIsActive"]) | ||
``` | ||
Here, the first line arranges the test data, setting the value in the raw JSON that we want to test the parsing of. | ||
|
||
The intention is to limit this to setting up any data that has a direct impact on what the unit test is specifically testing. You'll see at the top of our test files that we set up the raw json with all the data needed to not fail to be parsed. In this example, we only need to set the bIsActive field on the JSON for this test, as that is the value that we are testing the parsing of. | ||
|
||
The second line is acting, i.e. this is calling the code to test. In this example, this creates an instance of the GeniusZone class, which automatically parses the raw JSON passed in to the contructor. | ||
|
||
The third line is asserting whether the tested property contains the expected value. | ||
|
||
``` | ||
def test_when_iType_OnOffTimer_fSP_not_zero_setpoint_state_setpoint_set_true(self): | ||
"""Check that the setpoint is set to true when iType is OnOffTimer | ||
and fSP is not zero""" | ||
self.raw_json["fSP"] = 1.0 | ||
self.raw_json["iType"] = ZONE_TYPE.OnOffTimer | ||
genius_zone = GeniusZone(self._device_id, self.raw_json, self.hub) | ||
self.assertTrue(genius_zone.data["setpoint"]) | ||
``` | ||
In this example, the first two lines are setting up the data fields that are required for the test. Specifically, | ||
it sets the iType of the zone for this specific test case, then the value of fSP that is parsed. Both these values | ||
are required to be able to test the desired code branch. | ||
|
||
``` | ||
def test_when_iType_should_set_setpoint_state_setpoint_set_correctly(self): | ||
"Check that the setpoint is set for certain values of iType" | ||
setpoint = 21.0 | ||
self.raw_json["fSP"] = setpoint | ||
test_values = ( | ||
ZONE_TYPE.ControlSP, | ||
ZONE_TYPE.TPI | ||
) | ||
for zone_type in test_values: | ||
with self.subTest(zone_type=zone_type): | ||
self.raw_json["iType"] = zone_type | ||
genius_zone = GeniusZone(self._device_id, self.raw_json, self.hub) | ||
self.assertEqual(genius_zone.data["setpoint"], setpoint) | ||
``` | ||
In this more complicated example, we want to test that a specific value of the raw JSON field | ||
is successfully parsed, for a range of values of a separate field in the JSON. To do this, we are using | ||
sub-tests, which creates a test for each value in the test_values tuple. | ||
The arrange section here sets up a local variable for the setpoint, as this will be used to set the value on the JSON | ||
and to assert whether that value has been successfully parsed. | ||
The test values that will change for each sub-test are then set up, looped over, and a sub-test created. Within this sub-test, there are its own arrange, act, and assert sections. | ||
|
||
To manually run the tests from the command line, use this command: | ||
``` | ||
python -m unittest discover -p "*_test.py" | ||
``` | ||
|
||
Where the code to be tested contains other classes, make use of the Mock module to mock up | ||
these classes and any behaviours that you require for your tests. | ||
|
||
The idea is to test as little as possible in each test. To this aim, do not use multiple | ||
asserts in a unit test. |
243 changes: 243 additions & 0 deletions
243
tests/data_properties_v3_conversion/genius_zone_data_state_test.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,243 @@ | ||
""" | ||
Tests for the GeniusZone class | ||
""" | ||
|
||
import unittest | ||
from unittest.mock import Mock | ||
from geniushubclient.const import ( | ||
IMODE_TO_MODE, | ||
ZONE_MODE, | ||
ZONE_TYPE | ||
) | ||
from geniushubclient.zone import GeniusZone | ||
|
||
|
||
class GeniusZoneDataStateTests(unittest.TestCase): | ||
""" | ||
Test for the GeniusZone Class, state data. | ||
""" | ||
|
||
_device_id = "Device Id" | ||
_zone_name = "Zone Name" | ||
|
||
raw_json = { | ||
"iID": _device_id, | ||
"strName": _zone_name, | ||
"bIsActive": 0, | ||
"bInHeatEnabled": 0, | ||
"bOutRequestHeat": 0, | ||
"fBoostSP": 0, | ||
"fPV": 21.0, | ||
"fPV_offset": 0.0, | ||
"fSP": 14.0, | ||
"iBoostTimeRemaining": 0, | ||
"iFlagExpectedKit": 517, | ||
"iType": ZONE_TYPE.OnOffTimer, | ||
"iMode": ZONE_MODE.Off, | ||
"objFootprint": { | ||
"bIsNight": 0, | ||
"fFootprintAwaySP": 14.0, | ||
"iFootprintTmNightStart": 75600, | ||
"iProfile": 1, | ||
"lstSP": [{ | ||
"fSP": 16.0, | ||
"iDay": 0, | ||
"iTm": 0 | ||
}, { | ||
"fSP": 14.0, | ||
"iDay": 0, | ||
"iTm": 23400 | ||
}, { | ||
"fSP": 20.0, | ||
"iDay": 0, | ||
"iTm": 59700 | ||
}, { | ||
"fSP": 14.0, | ||
"iDay": 0, | ||
"iTm": 75000 | ||
}, { | ||
"fSP": 16.0, | ||
"iDay": 0, | ||
"iTm": 75600 | ||
} | ||
], | ||
"objReactive": { | ||
"fActivityLevel": 0.0 | ||
} | ||
}, | ||
"objTimer": [{ | ||
"fSP": 14.0, | ||
"iDay": 0, | ||
"iTm": -1 | ||
}], | ||
"trigger": { | ||
"reactive": 0, | ||
"output": 0 | ||
}, | ||
"warmupDuration": { | ||
"bEnable": "true", | ||
"bEnableCalcs": "true", | ||
"fRiseRate": 0.5, | ||
"iLagTime": 2420, | ||
"iRiseTime": 300, | ||
"iTotalTime": 2720 | ||
}, | ||
"zoneReactive": { | ||
"fActivityLevel": 0 | ||
}, | ||
"zoneSubType": 1 | ||
} | ||
|
||
def setUp(self): | ||
hub = Mock() | ||
hub.api_version = 3 | ||
self.hub = hub | ||
|
||
def test_when_bIsActive_is_false_then_state_bIsActive_false(self): | ||
"Check that the bIsActive is correctly set to false" | ||
|
||
self.raw_json["bIsActive"] = 0 | ||
|
||
genius_zone = GeniusZone(self._device_id, self.raw_json, self.hub) | ||
|
||
self.assertFalse(genius_zone.data["_state"]["bIsActive"]) | ||
|
||
def test_when_bIsActive_is_true_then_state_bIsActive_true(self): | ||
"Check that the bIsActive is correctly set to true" | ||
|
||
self.raw_json["bIsActive"] = 1 | ||
|
||
genius_zone = GeniusZone(self._device_id, self.raw_json, self.hub) | ||
|
||
self.assertTrue(genius_zone.data["_state"]["bIsActive"]) | ||
|
||
def test_when_bOutRequestHeat_is_false_then_state_bOutRequestHeat_false(self): | ||
"Check that the bOutRequestHeat is correctly set to false" | ||
|
||
self.raw_json["bOutRequestHeat"] = 0 | ||
|
||
genius_zone = GeniusZone(self._device_id, self.raw_json, self.hub) | ||
|
||
self.assertFalse(genius_zone.data["_state"]["bOutRequestHeat"]) | ||
|
||
def test_when_bOutRequestHeat_is_true_then_state_bOutRequestHeat_true(self): | ||
"Check that the bOutRequestHeat is correctly set to true" | ||
|
||
self.raw_json["bOutRequestHeat"] = 1 | ||
|
||
genius_zone = GeniusZone(self._device_id, self.raw_json, self.hub) | ||
|
||
self.assertTrue(genius_zone.data["_state"]["bOutRequestHeat"]) | ||
|
||
def test_when_iMode_set_then_state_mode_is_set_correctly(self): | ||
"Check that the mode is set on the class" | ||
|
||
for zone_mode, zone_mode_text in IMODE_TO_MODE.items(): | ||
with self.subTest(zone_mode=zone_mode, zone_mode_text=zone_mode_text): | ||
self.raw_json["iMode"] = zone_mode | ||
self.raw_json["zoneSubType"] = 1 | ||
|
||
genius_zone = GeniusZone(self._device_id, self.raw_json, self.hub) | ||
|
||
self.assertEqual(genius_zone.data["mode"], zone_mode_text) | ||
|
||
def test_when_iType_should_set_temperature_state_temperature_set_correctly(self): | ||
"Check that the temperature is set for certain values of iType" | ||
|
||
temperature = 20.0 | ||
self.raw_json["fPV"] = temperature | ||
|
||
test_values = ( | ||
ZONE_TYPE.ControlSP, | ||
ZONE_TYPE.TPI, | ||
ZONE_TYPE.Manager | ||
) | ||
|
||
for zone_type in test_values: | ||
with self.subTest(zone_type=zone_type): | ||
self.raw_json["iType"] = zone_type | ||
|
||
genius_zone = GeniusZone(self._device_id, self.raw_json, self.hub) | ||
|
||
self.assertEqual(genius_zone.data["temperature"], temperature) | ||
|
||
def test_when_iType_should_not_set_temperature_state_temperature_not_set(self): | ||
"Check that the temperature is not set for certain values of iType" | ||
|
||
self.raw_json["fPV"] = 20.0 | ||
|
||
test_values = ( | ||
ZONE_TYPE.OnOffTimer, | ||
ZONE_TYPE.ControlOnOffPID, | ||
ZONE_TYPE.Surrogate | ||
) | ||
|
||
for zone_type in test_values: | ||
with self.subTest(zone_type=zone_type): | ||
self.raw_json["iType"] = zone_type | ||
|
||
genius_zone = GeniusZone(self._device_id, self.raw_json, self.hub) | ||
|
||
self.assertFalse("temperature" in genius_zone.data) | ||
|
||
def test_when_iType_should_set_setpoint_state_setpoint_set_correctly(self): | ||
"Check that the setpoint is set for certain values of iType" | ||
|
||
setpoint = 21.0 | ||
self.raw_json["fSP"] = setpoint | ||
|
||
test_values = ( | ||
ZONE_TYPE.ControlSP, | ||
ZONE_TYPE.TPI | ||
) | ||
|
||
for zone_type in test_values: | ||
with self.subTest(zone_type=zone_type): | ||
self.raw_json["iType"] = zone_type | ||
|
||
genius_zone = GeniusZone(self._device_id, self.raw_json, self.hub) | ||
|
||
self.assertEqual(genius_zone.data["setpoint"], setpoint) | ||
|
||
def test_when_iType_should_not_set_setpoint_state_setpoint_not_set(self): | ||
"Check that the setpoint is not set for certain values of iType" | ||
|
||
self.raw_json["fSP"] = 21.0 | ||
|
||
test_values = ( | ||
ZONE_TYPE.Manager, | ||
ZONE_TYPE.ControlOnOffPID, | ||
ZONE_TYPE.Surrogate | ||
) | ||
|
||
for zone_type in test_values: | ||
with self.subTest(zone_type=zone_type): | ||
self.raw_json["iType"] = zone_type | ||
|
||
genius_zone = GeniusZone(self._device_id, self.raw_json, self.hub) | ||
|
||
self.assertFalse("setpoint" in genius_zone.data) | ||
|
||
def test_when_iType_OnOffTimer_fSP_not_zero_setpoint_state_setpoint_set_true(self): | ||
"""Check that the setpoint is set to true when iType is OnOffTimer | ||
and fSP is not zero""" | ||
|
||
self.raw_json["fSP"] = 1.0 | ||
self.raw_json["iType"] = ZONE_TYPE.OnOffTimer | ||
|
||
genius_zone = GeniusZone(self._device_id, self.raw_json, self.hub) | ||
|
||
self.assertTrue(genius_zone.data["setpoint"]) | ||
|
||
def test_when_iType_OnOffTimer_fSP_zero_setpoint_state_setpoint_set_false(self): | ||
"""Check that the setpoint is set to false when iType is OnOffTimer | ||
and fSP is zero""" | ||
|
||
self.raw_json["fSP"] = 0.0 | ||
self.raw_json["iType"] = ZONE_TYPE.OnOffTimer | ||
|
||
genius_zone = GeniusZone(self._device_id, self.raw_json, self.hub) | ||
|
||
self.assertFalse(genius_zone.data["setpoint"]) |