Просмотр исходного кода

Refactor unit tests to make writing new tests easier.

- Use mixins to cover some common sets of tests.
Covers most secondary light, lock and switch entities, and basic on/off toggling
of most non-climate primary entities.
Jason Rumney 4 лет назад
Родитель
Сommit
7b7aeaafb3
29 измененных файлов с 420 добавлено и 1526 удалено
  1. 203 0
      tests/devices/base_device_tests.py
  2. 3 26
      tests/devices/test_anko_fan.py
  3. 3 24
      tests/devices/test_arlec_fan.py
  4. 5 33
      tests/devices/test_awow_th213_thermostat.py
  5. 6 8
      tests/devices/test_beca_bhp6000_thermostat.py
  6. 8 10
      tests/devices/test_beca_bht002_thermostat.py
  7. 8 10
      tests/devices/test_beca_bht6000_thermostat.py
  8. 0 3
      tests/devices/test_carson_cb.py
  9. 15 73
      tests/devices/test_deta_fan.py
  10. 4 76
      tests/devices/test_eanons_humidifier.py
  11. 9 90
      tests/devices/test_electriq_12wminv_heatpump.py
  12. 8 62
      tests/devices/test_electriq_cd12_dehumidifier.py
  13. 15 93
      tests/devices/test_electriq_cd20_dehumidifier.py
  14. 20 120
      tests/devices/test_electriq_cd25_dehumidifier.py
  15. 8 80
      tests/devices/test_electriq_desd9lw_dehumidifier.py
  16. 21 94
      tests/devices/test_goldair_dehumidifier.py
  17. 6 44
      tests/devices/test_goldair_fan.py
  18. 3 31
      tests/devices/test_goldair_geco_heater.py
  19. 3 31
      tests/devices/test_goldair_gpcv_heater.py
  20. 6 82
      tests/devices/test_goldair_gpph_heater.py
  21. 3 27
      tests/devices/test_kogan_dehumidifier.py
  22. 3 31
      tests/devices/test_kogan_kahtp_heater.py
  23. 3 31
      tests/devices/test_kogan_kawfhtp_heater.py
  24. 20 137
      tests/devices/test_lexy_f501_fan.py
  25. 6 51
      tests/devices/test_purline_m100_heater.py
  26. 20 144
      tests/devices/test_renpho_rp_ap001s.py
  27. 5 33
      tests/devices/test_saswell_c16_thermostat.py
  28. 3 41
      tests/devices/test_smartplugv1.py
  29. 3 41
      tests/devices/test_smartplugv2.py

+ 203 - 0
tests/devices/base_device_tests.py

@@ -2,6 +2,11 @@ from unittest import IsolatedAsyncioTestCase
 from unittest.mock import AsyncMock, patch, PropertyMock
 from uuid import uuid4
 
+from homeassistant.components.light import COLOR_MODE_ONOFF
+from homeassistant.components.lock import STATE_LOCKED, STATE_UNLOCKED
+from homeassistant.components.switch import DEVICE_CLASS_SWITCH
+from homeassistant.const import STATE_UNAVAILABLE
+
 from custom_components.tuya_local.generic.climate import TuyaLocalClimate
 from custom_components.tuya_local.generic.fan import TuyaLocalFan
 from custom_components.tuya_local.generic.humidifier import TuyaLocalHumidifier
@@ -17,6 +22,8 @@ from custom_components.tuya_local.helpers.device_config import (
     possible_matches,
 )
 
+from ..helpers import assert_device_properties_set
+
 DEVICE_TYPES = {
     "climate": TuyaLocalClimate,
     "fan": TuyaLocalFan,
@@ -106,3 +113,199 @@ class TuyaDeviceTestCase(IsolatedAsyncioTestCase):
             await e.async_update()
             self.mock_device.async_refresh.assert_called_once()
             result.assert_awaited()
+
+
+# Mixins for common test functions
+
+
+class SwitchableTests:
+    def setUpSwitchable(self, dps, subject):
+        self.switch_dps = dps
+        self.switch_subject = subject
+
+    def test_switchable_is_on(self):
+        self.dps[self.switch_dps] = True
+        self.assertTrue(self.switch_subject.is_on)
+
+        self.dps[self.switch_dps] = False
+        self.assertFalse(self.switch_subject.is_on)
+
+        self.dps[self.switch_dps] = None
+        self.assertIsNone(self.switch_subject.is_on)
+
+    async def test_switchable_turn_on(self):
+        async with assert_device_properties_set(
+            self.switch_subject._device, {self.switch_dps: True}
+        ):
+            await self.switch_subject.async_turn_on()
+
+    async def test_switchable_turn_off(self):
+        async with assert_device_properties_set(
+            self.switch_subject._device, {self.switch_dps: False}
+        ):
+            await self.switch_subject.async_turn_off()
+
+    async def test_switchable_toggle(self):
+        self.dps[self.switch_dps] = False
+        async with assert_device_properties_set(
+            self.switch_subject._device, {self.switch_dps: True}
+        ):
+            await self.switch_subject.async_toggle()
+
+        self.dps[self.switch_dps] = True
+        async with assert_device_properties_set(
+            self.switch_subject._device, {self.switch_dps: False}
+        ):
+            await self.switch_subject.async_toggle()
+
+
+class BasicLightTests:
+    def setUpBasicLight(self, dps, subject):
+        self.basicLight = subject
+        self.basicLightDps = dps
+
+    def test_basic_light_supported_color_modes(self):
+        self.assertCountEqual(
+            self.basicLight.supported_color_modes,
+            [COLOR_MODE_ONOFF],
+        )
+
+    def test_basic_light_color_mode(self):
+        self.assertEqual(self.basicLight.color_mode, COLOR_MODE_ONOFF)
+
+    def test_light_has_no_brightness(self):
+        self.assertIsNone(self.basicLight.brightness)
+
+    def test_light_has_no_effects(self):
+        self.assertIsNone(self.basicLight.effect_list)
+        self.assertIsNone(self.basicLight.effect)
+
+    def test_basic_light_is_on(self):
+        self.dps[self.basicLightDps] = True
+        self.assertTrue(self.basicLight.is_on)
+        self.dps[self.basicLightDps] = False
+        self.assertFalse(self.basicLight.is_on)
+
+    async def test_basic_light_turn_on(self):
+        async with assert_device_properties_set(
+            self.basicLight._device, {self.basicLightDps: True}
+        ):
+            await self.basicLight.async_turn_on()
+
+    async def test_basic_light_turn_off(self):
+        async with assert_device_properties_set(
+            self.basicLight._device, {self.basicLightDps: False}
+        ):
+            await self.basicLight.async_turn_off()
+
+    async def test_basic_light_toggle_turns_on_when_it_was_off(self):
+        self.dps[self.basicLightDps] = False
+        async with assert_device_properties_set(
+            self.basicLight._device,
+            {self.basicLightDps: True},
+        ):
+            await self.basicLight.async_toggle()
+
+    async def test_basic_light_toggle_turns_off_when_it_was_on(self):
+        self.dps[self.basicLightDps] = True
+        async with assert_device_properties_set(
+            self.basicLight._device,
+            {self.basicLightDps: False},
+        ):
+            await self.basicLight.async_toggle()
+
+    def test_basic_light_state_attributes(self):
+        self.assertEqual(self.basicLight.device_state_attributes, {})
+
+
+class BasicLockTests:
+    def setUpBasicLock(self, dps, subject):
+        self.basicLock = subject
+        self.basicLockDps = dps
+
+    def test_basic_lock_state(self):
+        self.dps[self.basicLockDps] = True
+        self.assertEqual(self.basicLock.state, STATE_LOCKED)
+
+        self.dps[self.basicLockDps] = False
+        self.assertEqual(self.basicLock.state, STATE_UNLOCKED)
+
+        self.dps[self.basicLockDps] = None
+        self.assertEqual(self.basicLock.state, STATE_UNAVAILABLE)
+
+    def test_basic_lock_is_locked(self):
+        self.dps[self.basicLockDps] = True
+        self.assertTrue(self.basicLock.is_locked)
+
+        self.dps[self.basicLockDps] = False
+        self.assertFalse(self.basicLock.is_locked)
+
+        self.dps[self.basicLockDps] = None
+        self.assertFalse(self.basicLock.is_locked)
+
+    async def test_basic_lock_locks(self):
+        async with assert_device_properties_set(
+            self.basicLock._device,
+            {self.basicLockDps: True},
+        ):
+            await self.basicLock.async_lock()
+
+    async def test_basic_lock_unlocks(self):
+        async with assert_device_properties_set(
+            self.basicLock._device,
+            {self.basicLockDps: False},
+        ):
+            await self.basicLock.async_unlock()
+
+    def test_basic_lock_state_attributes(self):
+        self.assertEqual(self.basicLock.device_state_attributes, {})
+
+
+class BasicSwitchTests:
+    def setUpBasicSwitch(self, dps, subject):
+        self.basicSwitch = subject
+        self.basicSwitchDps = dps
+
+    def test_basic_switch_is_on(self):
+        self.dps[self.basicSwitchDps] = True
+        self.assertEqual(self.basicSwitch.is_on, True)
+
+        self.dps[self.basicSwitchDps] = False
+        self.assertEqual(self.basicSwitch.is_on, False)
+
+    async def test_basic_switch_turn_on(self):
+        async with assert_device_properties_set(
+            self.basicSwitch._device, {self.basicSwitchDps: True}
+        ):
+            await self.basicSwitch.async_turn_on()
+
+    async def test_basic_switch_turn_off(self):
+        async with assert_device_properties_set(
+            self.basicSwitch._device, {self.basicSwitchDps: False}
+        ):
+            await self.basicSwitch.async_turn_off()
+
+    async def test_basic_switch_toggle_turns_on_when_it_was_off(self):
+        self.dps[self.basicSwitchDps] = False
+
+        async with assert_device_properties_set(
+            self.basicSwitch._device, {self.basicSwitchDps: True}
+        ):
+            await self.basicSwitch.async_toggle()
+
+    async def test_basic_switch_toggle_turns_off_when_it_was_on(self):
+        self.dps[self.basicSwitchDps] = True
+
+        async with assert_device_properties_set(
+            self.basicSwitch._device, {self.basicSwitchDps: False}
+        ):
+            await self.basicSwitch.async_toggle()
+
+    def test_basic_switch_class_is_switch(self):
+        self.assertEqual(self.basicSwitch.device_class, DEVICE_CLASS_SWITCH)
+
+    def test_basic_switch_has_no_power_monitoring(self):
+        self.assertIsNone(self.basicSwitch.current_power_w)
+
+    def test_basic_switch_state_attributes(self):
+        self.assertEqual(self.basicSwitch.device_state_attributes, {})

+ 3 - 26
tests/devices/test_anko_fan.py

@@ -4,11 +4,9 @@ from homeassistant.components.fan import (
     SUPPORT_SET_SPEED,
 )
 
-from homeassistant.const import STATE_UNAVAILABLE
-
 from ..const import ANKO_FAN_PAYLOAD
 from ..helpers import assert_device_properties_set
-from .base_device_tests import TuyaDeviceTestCase
+from .base_device_tests import SwitchableTests, TuyaDeviceTestCase
 
 SWITCH_DPS = "1"
 PRESET_DPS = "2"
@@ -17,12 +15,13 @@ OSCILLATE_DPS = "4"
 TIMER_DPS = "6"
 
 
-class TestAnkoFan(TuyaDeviceTestCase):
+class TestAnkoFan(SwitchableTests, TuyaDeviceTestCase):
     __test__ = True
 
     def setUp(self):
         self.setUpForConfig("anko_fan.yaml", ANKO_FAN_PAYLOAD)
         self.subject = self.entities["fan"]
+        self.setUpSwitchable(SWITCH_DPS, self.subject)
 
     def test_supported_features(self):
         self.assertEqual(
@@ -30,28 +29,6 @@ class TestAnkoFan(TuyaDeviceTestCase):
             SUPPORT_OSCILLATE | SUPPORT_PRESET_MODE | SUPPORT_SET_SPEED,
         )
 
-    def test_is_on(self):
-        self.dps[SWITCH_DPS] = True
-        self.assertTrue(self.subject.is_on)
-
-        self.dps[SWITCH_DPS] = False
-        self.assertFalse(self.subject.is_on)
-
-        self.dps[SWITCH_DPS] = None
-        self.assertIsNone(self.subject.is_on)
-
-    async def test_turn_on(self):
-        async with assert_device_properties_set(
-            self.subject._device, {SWITCH_DPS: True}
-        ):
-            await self.subject.async_turn_on()
-
-    async def test_turn_off(self):
-        async with assert_device_properties_set(
-            self.subject._device, {SWITCH_DPS: False}
-        ):
-            await self.subject.async_turn_off()
-
     def test_preset_mode(self):
         self.dps[PRESET_DPS] = "normal"
         self.assertEqual(self.subject.preset_mode, "normal")

+ 3 - 24
tests/devices/test_arlec_fan.py

@@ -10,7 +10,7 @@ from homeassistant.const import STATE_UNAVAILABLE
 
 from ..const import ARLEC_FAN_PAYLOAD
 from ..helpers import assert_device_properties_set
-from .base_device_tests import TuyaDeviceTestCase
+from .base_device_tests import SwitchableTests, TuyaDeviceTestCase
 
 SWITCH_DPS = "1"
 SPEED_DPS = "3"
@@ -19,13 +19,14 @@ PRESET_DPS = "102"
 TIMER_DPS = "103"
 
 
-class TestArlecFan(TuyaDeviceTestCase):
+class TestArlecFan(SwitchableTests, TuyaDeviceTestCase):
     __test__ = True
 
     def setUp(self):
         self.setUpForConfig("arlec_fan.yaml", ARLEC_FAN_PAYLOAD)
         self.subject = self.entities["fan"]
         self.timer = self.entities["select_timer"]
+        self.setUpSwitchable(SWITCH_DPS, self.subject)
 
     def test_supported_features(self):
         self.assertEqual(
@@ -33,28 +34,6 @@ class TestArlecFan(TuyaDeviceTestCase):
             SUPPORT_DIRECTION | SUPPORT_PRESET_MODE | SUPPORT_SET_SPEED,
         )
 
-    def test_is_on(self):
-        self.dps[SWITCH_DPS] = True
-        self.assertTrue(self.subject.is_on)
-
-        self.dps[SWITCH_DPS] = False
-        self.assertFalse(self.subject.is_on)
-
-        self.dps[SWITCH_DPS] = None
-        self.assertIsNone(self.subject.is_on)
-
-    async def test_turn_on(self):
-        async with assert_device_properties_set(
-            self.subject._device, {SWITCH_DPS: True}
-        ):
-            await self.subject.async_turn_on()
-
-    async def test_turn_off(self):
-        async with assert_device_properties_set(
-            self.subject._device, {SWITCH_DPS: False}
-        ):
-            await self.subject.async_turn_off()
-
     def test_preset_mode(self):
         self.dps[PRESET_DPS] = "normal"
         self.assertEqual(self.subject.preset_mode, "normal")

+ 5 - 33
tests/devices/test_awow_th213_thermostat.py

@@ -12,7 +12,7 @@ from homeassistant.const import STATE_UNAVAILABLE
 
 from ..const import TH213_THERMOSTAT_PAYLOAD
 from ..helpers import assert_device_properties_set
-from .base_device_tests import TuyaDeviceTestCase
+from .base_device_tests import BasicLockTests, TuyaDeviceTestCase
 
 HVACMODE_DPS = "1"
 TEMPERATURE_DPS = "2"
@@ -30,13 +30,13 @@ UNKNOWN108_DPS = "108"
 UNKNOWN110_DPS = "110"
 
 
-class TestAwowTH213Thermostat(TuyaDeviceTestCase):
+class TestAwowTH213Thermostat(BasicLockTests, TuyaDeviceTestCase):
     __test__ = True
 
     def setUp(self):
         self.setUpForConfig("awow_th213_thermostat.yaml", TH213_THERMOSTAT_PAYLOAD)
         self.subject = self.entities.get("climate")
-        self.lock = self.entities.get("lock_child_lock")
+        self.setUpBasicLock(LOCK_DPS, self.entities.get("lock_child_lock"))
 
     def test_supported_features(self):
         self.assertEqual(
@@ -52,10 +52,10 @@ class TestAwowTH213Thermostat(TuyaDeviceTestCase):
         self.assertEqual(self.subject.icon, "mdi:thermometer-off")
 
         self.dps[LOCK_DPS] = True
-        self.assertEqual(self.lock.icon, "mdi:hand-back-right-off")
+        self.assertEqual(self.basicLock.icon, "mdi:hand-back-right-off")
 
         self.dps[LOCK_DPS] = False
-        self.assertEqual(self.lock.icon, "mdi:hand-back-right")
+        self.assertEqual(self.basicLock.icon, "mdi:hand-back-right")
 
     def test_temperature_unit_returns_device_temperature_unit(self):
         self.assertEqual(
@@ -238,31 +238,3 @@ class TestAwowTH213Thermostat(TuyaDeviceTestCase):
                 "unknown_110": 110,
             },
         )
-
-    def test_lock_state(self):
-        self.dps[LOCK_DPS] = True
-        self.assertEqual(self.lock.state, STATE_LOCKED)
-
-        self.dps[LOCK_DPS] = False
-        self.assertEqual(self.lock.state, STATE_UNLOCKED)
-
-        self.dps[LOCK_DPS] = None
-        self.assertEqual(self.lock.state, STATE_UNAVAILABLE)
-
-    def test_lock_is_locked(self):
-        self.dps[LOCK_DPS] = True
-        self.assertTrue(self.lock.is_locked)
-
-        self.dps[LOCK_DPS] = False
-        self.assertFalse(self.lock.is_locked)
-
-        self.dps[LOCK_DPS] = None
-        self.assertFalse(self.lock.is_locked)
-
-    async def test_lock_locks(self):
-        async with assert_device_properties_set(self.lock._device, {LOCK_DPS: True}):
-            await self.lock.async_lock()
-
-    async def test_lock_unlocks(self):
-        async with assert_device_properties_set(self.lock._device, {LOCK_DPS: False}):
-            await self.lock.async_unlock()

+ 6 - 8
tests/devices/test_beca_bhp6000_thermostat.py

@@ -12,7 +12,7 @@ from homeassistant.const import STATE_UNAVAILABLE, TEMP_CELSIUS, TEMP_FAHRENHEIT
 
 from ..const import BECA_BHP6000_PAYLOAD
 from ..helpers import assert_device_properties_set
-from .base_device_tests import TuyaDeviceTestCase
+from .base_device_tests import BasicLightTests, BasicLockTests, TuyaDeviceTestCase
 
 LIGHT_DPS = "1"
 TEMPERATURE_DPS = "2"
@@ -23,14 +23,14 @@ FAN_DPS = "6"
 LOCK_DPS = "7"
 
 
-class TestBecaBHP6000Thermostat(TuyaDeviceTestCase):
+class TestBecaBHP6000Thermostat(BasicLightTests, BasicLockTests, TuyaDeviceTestCase):
     __test__ = True
 
     def setUp(self):
         self.setUpForConfig("beca_bhp6000_thermostat_f.yaml", BECA_BHP6000_PAYLOAD)
         self.subject = self.entities.get("climate")
-        self.light = self.entities.get("light_display")
-        self.lock = self.entities.get("lock_child_lock")
+        self.setUpBasicLight(LIGHT_DPS, self.entities.get("light_display"))
+        self.setUpBasicLock(LOCK_DPS, self.entities.get("lock_child_lock"))
 
     def test_supported_features(self):
         self.assertEqual(
@@ -170,8 +170,6 @@ class TestBecaBHP6000Thermostat(TuyaDeviceTestCase):
 
     def test_device_state_attribures(self):
         self.assertEqual(self.subject.device_state_attributes, {})
-        self.assertEqual(self.light.device_state_attributes, {})
-        self.assertEqual(self.lock.device_state_attributes, {})
 
     def test_icons(self):
         self.dps[HVACMODE_DPS] = 1
@@ -186,9 +184,9 @@ class TestBecaBHP6000Thermostat(TuyaDeviceTestCase):
         self.assertEqual(self.subject.icon, "mdi:hvac")
 
         self.dps[LIGHT_DPS] = True
-        self.assertEqual(self.light.icon, "mdi:led-on")
+        self.assertEqual(self.basicLight.icon, "mdi:led-on")
         self.dps[LIGHT_DPS] = False
-        self.assertEqual(self.light.icon, "mdi:led-off")
+        self.assertEqual(self.basicLight.icon, "mdi:led-off")
 
 
 class TestBecaBHP6000ThermostatC(TuyaDeviceTestCase):

+ 8 - 10
tests/devices/test_beca_bht002_thermostat.py

@@ -10,7 +10,7 @@ from homeassistant.const import STATE_UNAVAILABLE
 
 from ..const import BECA_BHT002_PAYLOAD
 from ..helpers import assert_device_properties_set
-from .base_device_tests import TuyaDeviceTestCase
+from .base_device_tests import BasicLightTests, BasicLockTests, TuyaDeviceTestCase
 
 LIGHT_DPS = "1"
 TEMPERATURE_DPS = "2"
@@ -22,7 +22,7 @@ FLOOR_DPS = "102"
 UNKNOWN104_DPS = "104"
 
 
-class TestBecaBHT002Thermostat(TuyaDeviceTestCase):
+class TestBecaBHT002Thermostat(BasicLightTests, BasicLockTests, TuyaDeviceTestCase):
     __test__ = True
 
     def setUp(self):
@@ -31,8 +31,8 @@ class TestBecaBHT002Thermostat(TuyaDeviceTestCase):
             BECA_BHT002_PAYLOAD,
         )
         self.subject = self.entities.get("climate")
-        self.light = self.entities.get("light_display")
-        self.lock = self.entities.get("lock_child_lock")
+        self.setUpBasicLight(LIGHT_DPS, self.entities.get("light_display"))
+        self.setUpBasicLock(LOCK_DPS, self.entities.get("lock_child_lock"))
 
     def test_supported_features(self):
         self.assertEqual(
@@ -138,16 +138,14 @@ class TestBecaBHT002Thermostat(TuyaDeviceTestCase):
             self.subject.device_state_attributes,
             {"floor_temperature": 22.5, "unknown_104": False},
         )
-        self.assertDictEqual(self.light.device_state_attributes, {})
-        self.assertDictEqual(self.lock.device_state_attributes, {})
 
     def test_icons(self):
         self.dps[LIGHT_DPS] = True
-        self.assertEqual(self.light.icon, "mdi:led-on")
+        self.assertEqual(self.basicLight.icon, "mdi:led-on")
         self.dps[LIGHT_DPS] = False
-        self.assertEqual(self.light.icon, "mdi:led-off")
+        self.assertEqual(self.basicLight.icon, "mdi:led-off")
 
         self.dps[LOCK_DPS] = True
-        self.assertEqual(self.lock.icon, "mdi:hand-back-right-off")
+        self.assertEqual(self.basicLock.icon, "mdi:hand-back-right-off")
         self.dps[LOCK_DPS] = False
-        self.assertEqual(self.lock.icon, "mdi:hand-back-right")
+        self.assertEqual(self.basicLock.icon, "mdi:hand-back-right")

+ 8 - 10
tests/devices/test_beca_bht6000_thermostat.py

@@ -10,7 +10,7 @@ from homeassistant.const import STATE_UNAVAILABLE
 
 from ..const import BECA_BHT6000_PAYLOAD
 from ..helpers import assert_device_properties_set
-from .base_device_tests import TuyaDeviceTestCase
+from .base_device_tests import BasicLightTests, BasicLockTests, TuyaDeviceTestCase
 
 LIGHT_DPS = "1"
 TEMPERATURE_DPS = "2"
@@ -23,7 +23,7 @@ UNKNOWN103_DPS = "103"
 UNKNOWN104_DPS = "104"
 
 
-class TestBecaBHT6000Thermostat(TuyaDeviceTestCase):
+class TestBecaBHT6000Thermostat(BasicLightTests, BasicLockTests, TuyaDeviceTestCase):
     __test__ = True
 
     def setUp(self):
@@ -32,8 +32,8 @@ class TestBecaBHT6000Thermostat(TuyaDeviceTestCase):
             BECA_BHT6000_PAYLOAD,
         )
         self.subject = self.entities.get("climate")
-        self.light = self.entities.get("light_display")
-        self.lock = self.entities.get("lock_child_lock")
+        self.setUpBasicLight(LIGHT_DPS, self.entities.get("light_display"))
+        self.setUpBasicLock(LOCK_DPS, self.entities.get("lock_child_lock"))
 
     def test_supported_features(self):
         self.assertEqual(
@@ -140,16 +140,14 @@ class TestBecaBHT6000Thermostat(TuyaDeviceTestCase):
             self.subject.device_state_attributes,
             {"floor_temperature": 22.5, "unknown_103": "103", "unknown_104": False},
         )
-        self.assertDictEqual(self.light.device_state_attributes, {})
-        self.assertDictEqual(self.lock.device_state_attributes, {})
 
     def test_icons(self):
         self.dps[LIGHT_DPS] = True
-        self.assertEqual(self.light.icon, "mdi:led-on")
+        self.assertEqual(self.basicLight.icon, "mdi:led-on")
         self.dps[LIGHT_DPS] = False
-        self.assertEqual(self.light.icon, "mdi:led-off")
+        self.assertEqual(self.basicLight.icon, "mdi:led-off")
 
         self.dps[LOCK_DPS] = True
-        self.assertEqual(self.lock.icon, "mdi:hand-back-right-off")
+        self.assertEqual(self.basicLock.icon, "mdi:hand-back-right-off")
         self.dps[LOCK_DPS] = False
-        self.assertEqual(self.lock.icon, "mdi:hand-back-right")
+        self.assertEqual(self.basicLock.icon, "mdi:hand-back-right")

+ 0 - 3
tests/devices/test_carson_cb.py

@@ -8,7 +8,6 @@ from homeassistant.components.climate.const import (
     SUPPORT_SWING_MODE,
     SUPPORT_TARGET_TEMPERATURE,
 )
-from homeassistant.components.light import COLOR_MODE_ONOFF
 from homeassistant.const import STATE_UNAVAILABLE, TEMP_CELSIUS, TEMP_FAHRENHEIT
 
 from ..const import CARSON_CB_PAYLOAD
@@ -35,8 +34,6 @@ class TestCarsonCBHeatpump(TuyaDeviceTestCase):
     def setUp(self):
         self.setUpForConfig("carson_cb.yaml", CARSON_CB_PAYLOAD)
         self.subject = self.entities.get("climate")
-        self.light = self.entities.get("light")
-        self.switch = self.entities.get("switch")
 
     def test_supported_features(self):
         self.assertEqual(

+ 15 - 73
tests/devices/test_deta_fan.py

@@ -3,7 +3,12 @@ from homeassistant.components.light import COLOR_MODE_ONOFF
 
 from ..const import DETA_FAN_PAYLOAD
 from ..helpers import assert_device_properties_set
-from .base_device_tests import TuyaDeviceTestCase
+from .base_device_tests import (
+    BasicLightTests,
+    BasicSwitchTests,
+    SwitchableTests,
+    TuyaDeviceTestCase,
+)
 
 SWITCH_DPS = "1"
 SPEED_DPS = "3"
@@ -13,14 +18,17 @@ TIMER_DPS = "102"
 LIGHT_TIMER_DPS = "103"
 
 
-class TestDetaFan(TuyaDeviceTestCase):
+class TestDetaFan(
+    BasicLightTests, BasicSwitchTests, SwitchableTests, TuyaDeviceTestCase
+):
     __test__ = True
 
     def setUp(self):
         self.setUpForConfig("deta_fan.yaml", DETA_FAN_PAYLOAD)
         self.subject = self.entities["fan"]
-        self.light = self.entities["light"]
-        self.switch = self.entities["switch_master"]
+        self.setUpSwitchable(SWITCH_DPS, self.subject)
+        self.setUpBasicLight(LIGHT_DPS, self.entities["light"])
+        self.setUpBasicSwitch(MASTER_DPS, self.entities["switch_master"])
 
     def test_supported_features(self):
         self.assertEqual(
@@ -28,28 +36,6 @@ class TestDetaFan(TuyaDeviceTestCase):
             SUPPORT_SET_SPEED,
         )
 
-    def test_is_on(self):
-        self.dps[SWITCH_DPS] = True
-        self.assertTrue(self.subject.is_on)
-
-        self.dps[SWITCH_DPS] = False
-        self.assertFalse(self.subject.is_on)
-
-        self.dps[SWITCH_DPS] = None
-        self.assertIsNone(self.subject.is_on)
-
-    async def test_turn_on(self):
-        async with assert_device_properties_set(
-            self.subject._device, {SWITCH_DPS: True}
-        ):
-            await self.subject.async_turn_on()
-
-    async def test_turn_off(self):
-        async with assert_device_properties_set(
-            self.subject._device, {SWITCH_DPS: False}
-        ):
-            await self.subject.async_turn_off()
-
     def test_speed(self):
         self.dps[SPEED_DPS] = "1"
         self.assertAlmostEqual(self.subject.percentage, 33.3, 1)
@@ -73,52 +59,8 @@ class TestDetaFan(TuyaDeviceTestCase):
 
     def test_device_state_attributes(self):
         self.dps[TIMER_DPS] = "5"
-        self.dps[LIGHT_TIMER_DPS] = "6"
         self.assertEqual(self.subject.device_state_attributes, {"timer": 5})
-        self.assertEqual(self.light.device_state_attributes, {"timer": 6})
-
-    def test_light_supported_color_modes(self):
-        self.assertCountEqual(
-            self.light.supported_color_modes,
-            [COLOR_MODE_ONOFF],
-        )
-
-    def test_light_color_mode(self):
-        self.assertEqual(self.light.color_mode, COLOR_MODE_ONOFF)
 
-    def test_light_is_on(self):
-        self.dps[LIGHT_DPS] = True
-        self.assertTrue(self.light.is_on)
-
-        self.dps[LIGHT_DPS] = False
-        self.assertFalse(self.light.is_on)
-
-    async def test_light_turn_on(self):
-        async with assert_device_properties_set(self.light._device, {LIGHT_DPS: True}):
-            await self.light.async_turn_on()
-
-    async def test_light_turn_off(self):
-        async with assert_device_properties_set(self.light._device, {LIGHT_DPS: False}):
-            await self.light.async_turn_off()
-
-    def test_switch_is_on(self):
-        self.dps[MASTER_DPS] = True
-        self.assertTrue(self.switch.is_on)
-
-        self.dps[MASTER_DPS] = False
-        self.assertFalse(self.switch.is_on)
-
-        self.dps[MASTER_DPS] = None
-        self.assertIsNone(self.switch.is_on)
-
-    async def test_switch_turn_on(self):
-        async with assert_device_properties_set(
-            self.switch._device, {MASTER_DPS: True}
-        ):
-            await self.switch.async_turn_on()
-
-    async def test_switch_turn_off(self):
-        async with assert_device_properties_set(
-            self.light._device, {MASTER_DPS: False}
-        ):
-            await self.switch.async_turn_off()
+    def test_basic_light_state_attributes(self):
+        self.dps[LIGHT_TIMER_DPS] = "6"
+        self.assertEqual(self.basicLight.device_state_attributes, {"timer": 6})

+ 4 - 76
tests/devices/test_eanons_humidifier.py

@@ -17,12 +17,11 @@ from homeassistant.components.humidifier.const import (
     MODE_SLEEP,
     SUPPORT_MODES,
 )
-from homeassistant.components.switch import DEVICE_CLASS_SWITCH
 from homeassistant.const import STATE_UNAVAILABLE
 
 from ..const import EANONS_HUMIDIFIER_PAYLOAD
 from ..helpers import assert_device_properties_set
-from .base_device_tests import TuyaDeviceTestCase
+from .base_device_tests import BasicSwitchTests, SwitchableTests, TuyaDeviceTestCase
 
 FANMODE_DPS = "2"
 TIMERHR_DPS = "3"
@@ -35,15 +34,16 @@ CURRENTHUMID_DPS = "16"
 SWITCH_DPS = "22"
 
 
-class TestEanonsHumidifier(TuyaDeviceTestCase):
+class TestEanonsHumidifier(BasicSwitchTests, SwitchableTests, TuyaDeviceTestCase):
     __test__ = True
 
     def setUp(self):
         self.setUpForConfig("eanons_humidifier.yaml", EANONS_HUMIDIFIER_PAYLOAD)
         self.subject = self.entities["humidifier"]
+        self.setUpSwitchable(HVACMODE_DPS, self.subject)
         self.climate = self.entities["climate"]
-        self.switch = self.entities["switch_uv_sterilization"]
         self.fan = self.entities["fan_intensity"]
+        self.setUpBasicSwitch(SWITCH_DPS, self.entities["switch_uv_sterilization"])
 
     def test_supported_features(self):
         self.assertEqual(
@@ -99,18 +99,6 @@ class TestEanonsHumidifier(TuyaDeviceTestCase):
     def test_climate_hvac_modes(self):
         self.assertCountEqual(self.climate.hvac_modes, [HVAC_MODE_OFF, HVAC_MODE_DRY])
 
-    def test_is_on(self):
-        self.dps[HVACMODE_DPS] = True
-        self.assertTrue(self.subject.is_on)
-        self.assertTrue(self.fan.is_on)
-        self.dps[HVACMODE_DPS] = False
-        self.assertFalse(self.subject.is_on)
-        self.assertFalse(self.fan.is_on)
-
-        self.dps[HVACMODE_DPS] = None
-        self.assertIsNone(self.subject.is_on)
-        self.assertIsNone(self.fan.is_on)
-
     async def test_climate_turn_on(self):
         async with assert_device_properties_set(
             self.climate._device, {HVACMODE_DPS: True}
@@ -123,18 +111,6 @@ class TestEanonsHumidifier(TuyaDeviceTestCase):
         ):
             await self.climate.async_set_hvac_mode(HVAC_MODE_OFF)
 
-    async def test_turn_on(self):
-        async with assert_device_properties_set(
-            self.subject._device, {HVACMODE_DPS: True}
-        ):
-            await self.subject.async_turn_on()
-
-    async def test_turn_off(self):
-        async with assert_device_properties_set(
-            self.subject._device, {HVACMODE_DPS: False}
-        ):
-            await self.subject.async_turn_off()
-
     async def test_fan_turn_on(self):
         async with assert_device_properties_set(
             self.subject._device, {HVACMODE_DPS: True}
@@ -331,51 +307,3 @@ class TestEanonsHumidifier(TuyaDeviceTestCase):
             {FANMODE_DPS: "small"},
         ):
             await self.climate.async_set_fan_mode(FAN_LOW)
-
-    def test_switch_class_is_switch(self):
-        self.assertEqual(self.switch.device_class, DEVICE_CLASS_SWITCH)
-
-    def test_switch_is_on(self):
-        self.dps[SWITCH_DPS] = True
-        self.assertTrue(self.switch.is_on)
-
-        self.dps[SWITCH_DPS] = False
-        self.assertFalse(self.switch.is_on)
-
-    def test_switch_is_on_when_unavailable(self):
-        self.dps[SWITCH_DPS] = None
-        self.assertIsNone(self.switch.is_on)
-
-    async def test_switch_turn_on(self):
-        async with assert_device_properties_set(
-            self.switch._device, {SWITCH_DPS: True}
-        ):
-            await self.switch.async_turn_on()
-
-    async def test_switch_turn_off(self):
-        async with assert_device_properties_set(
-            self.switch._device, {SWITCH_DPS: False}
-        ):
-            await self.switch.async_turn_off()
-
-    async def test_toggle_turns_the_switch_on_when_it_was_off(self):
-        self.dps[SWITCH_DPS] = False
-
-        async with assert_device_properties_set(
-            self.switch._device, {SWITCH_DPS: True}
-        ):
-            await self.switch.async_toggle()
-
-    async def test_toggle_turns_the_switch_off_when_it_was_on(self):
-        self.dps[SWITCH_DPS] = True
-
-        async with assert_device_properties_set(
-            self.switch._device, {SWITCH_DPS: False}
-        ):
-            await self.switch.async_toggle()
-
-    def test_switch_returns_none_for_power(self):
-        self.assertIsNone(self.switch.current_power_w)
-
-    def test_switch_state_attributes_set(self):
-        self.assertEqual(self.switch.device_state_attributes, {})

+ 9 - 90
tests/devices/test_electriq_12wminv_heatpump.py

@@ -14,7 +14,7 @@ from homeassistant.const import STATE_UNAVAILABLE
 
 from ..const import ELECTRIQ_12WMINV_HEATPUMP_PAYLOAD
 from ..helpers import assert_device_properties_set
-from .base_device_tests import TuyaDeviceTestCase
+from .base_device_tests import BasicLightTests, BasicSwitchTests, TuyaDeviceTestCase
 
 POWER_DPS = "1"
 TEMPERATURE_DPS = "2"
@@ -34,7 +34,9 @@ UNKNOWN109_DPS = "109"
 UNKNOWN110_DPS = "110"
 
 
-class TestElectriq12WMINVHeatpump(TuyaDeviceTestCase):
+class TestElectriq12WMINVHeatpump(
+    BasicLightTests, BasicSwitchTests, TuyaDeviceTestCase
+):
     __test__ = True
 
     def setUp(self):
@@ -42,8 +44,8 @@ class TestElectriq12WMINVHeatpump(TuyaDeviceTestCase):
             "electriq_12wminv_heatpump.yaml", ELECTRIQ_12WMINV_HEATPUMP_PAYLOAD
         )
         self.subject = self.entities.get("climate")
-        self.light = self.entities.get("light_display")
-        self.switch = self.entities.get("switch_sleep")
+        self.setUpBasicLight(LIGHT_DPS, self.entities.get("light_display"))
+        self.setUpBasicSwitch(SWITCH_DPS, self.entities.get("switch_sleep"))
 
     def test_supported_features(self):
         self.assertEqual(
@@ -304,95 +306,12 @@ class TestElectriq12WMINVHeatpump(TuyaDeviceTestCase):
             },
         )
 
-    def test_light_state_attributes(self):
-        self.assertEqual(self.light.device_state_attributes, {})
-
-    def test_light_supported_color_modes(self):
-        self.assertCountEqual(
-            self.light.supported_color_modes,
-            [COLOR_MODE_ONOFF],
-        )
-
-    def test_light_color_mode(self):
-        self.assertEqual(self.light.color_mode, COLOR_MODE_ONOFF)
-
-    def test_light_is_on(self):
-        self.dps[LIGHT_DPS] = True
-        self.assertTrue(self.light.is_on)
-        self.dps[LIGHT_DPS] = False
-        self.assertFalse(self.light.is_on)
-
-    async def test_light_turn_on(self):
-        async with assert_device_properties_set(
-            self.light._device,
-            {LIGHT_DPS: True},
-        ):
-            await self.light.async_turn_on()
-
-    async def test_light_turn_off(self):
-        async with assert_device_properties_set(
-            self.light._device,
-            {LIGHT_DPS: False},
-        ):
-            await self.light.async_turn_off()
-
-    async def test_toggle_turns_the_light_on_when_it_was_off(self):
-        self.dps[LIGHT_DPS] = False
-
-        async with assert_device_properties_set(self.light._device, {LIGHT_DPS: True}):
-            await self.light.async_toggle()
-
-    async def test_toggle_turns_the_light_off_when_it_was_on(self):
-        self.dps[LIGHT_DPS] = True
-
-        async with assert_device_properties_set(self.light._device, {LIGHT_DPS: False}):
-            await self.light.async_toggle()
-
     def test_light_icon(self):
         self.dps[LIGHT_DPS] = True
-        self.assertEqual(self.light.icon, "mdi:led-on")
+        self.assertEqual(self.basicLight.icon, "mdi:led-on")
 
         self.dps[LIGHT_DPS] = False
-        self.assertEqual(self.light.icon, "mdi:led-off")
-
-    def test_switch_state_attributes(self):
-        self.assertEqual(self.switch.device_state_attributes, {})
-
-    def test_switch_is_on(self):
-        self.dps[SWITCH_DPS] = True
-        self.assertTrue(self.switch.is_on)
-        self.dps[SWITCH_DPS] = False
-        self.assertFalse(self.switch.is_on)
-
-    async def test_switch_turn_on(self):
-        async with assert_device_properties_set(
-            self.switch._device,
-            {SWITCH_DPS: True},
-        ):
-            await self.switch.async_turn_on()
-
-    async def test_switch_turn_off(self):
-        async with assert_device_properties_set(
-            self.switch._device,
-            {SWITCH_DPS: False},
-        ):
-            await self.switch.async_turn_off()
-
-    async def test_toggle_turns_the_switch_on_when_it_was_off(self):
-        self.dps[SWITCH_DPS] = False
-
-        async with assert_device_properties_set(
-            self.switch._device, {SWITCH_DPS: True}
-        ):
-            await self.switch.async_toggle()
-
-    async def test_toggle_turns_the_switch_off_when_it_was_on(self):
-        self.dps[SWITCH_DPS] = True
-
-        async with assert_device_properties_set(
-            self.switch._device, {SWITCH_DPS: False}
-        ):
-            await self.switch.async_toggle()
+        self.assertEqual(self.basicLight.icon, "mdi:led-off")
 
     def test_switch_icon(self):
-        self.assertEqual(self.switch.icon, "mdi:power-sleep")
+        self.assertEqual(self.basicSwitch.icon, "mdi:power-sleep")

+ 8 - 62
tests/devices/test_electriq_cd12_dehumidifier.py

@@ -3,7 +3,7 @@ from homeassistant.components.light import COLOR_MODE_ONOFF
 
 from ..const import ELECTRIQ_CD12PW_DEHUMIDIFIER_PAYLOAD
 from ..helpers import assert_device_properties_set
-from .base_device_tests import TuyaDeviceTestCase
+from .base_device_tests import BasicLightTests, SwitchableTests, TuyaDeviceTestCase
 
 SWITCH_DPS = "1"
 MODE_DPS = "2"
@@ -13,7 +13,9 @@ LIGHT_DPS = "101"
 CURRENTTEMP_DPS = "103"
 
 
-class TestElectriqCD20ProDehumidifier(TuyaDeviceTestCase):
+class TestElectriqCD20ProDehumidifier(
+    BasicLightTests, SwitchableTests, TuyaDeviceTestCase
+):
     __test__ = True
 
     def setUp(self):
@@ -21,7 +23,8 @@ class TestElectriqCD20ProDehumidifier(TuyaDeviceTestCase):
             "electriq_cd12pw_dehumidifier.yaml", ELECTRIQ_CD12PW_DEHUMIDIFIER_PAYLOAD
         )
         self.subject = self.entities.get("humidifier")
-        self.light = self.entities.get("light_display")
+        self.setUpSwitchable(SWITCH_DPS, self.subject)
+        self.setUpBasicLight(LIGHT_DPS, self.entities.get("light_display"))
 
     def test_supported_features(self):
         self.assertEqual(self.subject.supported_features, SUPPORT_MODES)
@@ -42,9 +45,9 @@ class TestElectriqCD20ProDehumidifier(TuyaDeviceTestCase):
         self.assertEqual(self.subject.icon, "mdi:air-purifier")
 
         self.dps[LIGHT_DPS] = True
-        self.assertEqual(self.light.icon, "mdi:led-on")
+        self.assertEqual(self.basicLight.icon, "mdi:led-on")
         self.dps[LIGHT_DPS] = False
-        self.assertEqual(self.light.icon, "mdi:led-off")
+        self.assertEqual(self.basicLight.icon, "mdi:led-off")
 
     def test_min_target_humidity(self):
         self.assertEqual(self.subject.min_humidity, 35)
@@ -70,28 +73,6 @@ class TestElectriqCD20ProDehumidifier(TuyaDeviceTestCase):
         ):
             await self.subject.async_set_humidity(52)
 
-    def test_is_on(self):
-        self.dps[SWITCH_DPS] = True
-        self.assertTrue(self.subject.is_on)
-
-        self.dps[SWITCH_DPS] = False
-        self.assertFalse(self.subject.is_on)
-
-        self.dps[SWITCH_DPS] = None
-        self.assertIsNone(self.subject.is_on)
-
-    async def test_turn_on(self):
-        async with assert_device_properties_set(
-            self.subject._device, {SWITCH_DPS: True}
-        ):
-            await self.subject.async_turn_on()
-
-    async def test_turn_off(self):
-        async with assert_device_properties_set(
-            self.subject._device, {SWITCH_DPS: False}
-        ):
-            await self.subject.async_turn_off()
-
     def test_mode(self):
         self.dps[MODE_DPS] = "auto"
         self.assertEqual(self.subject.mode, "Auto")
@@ -118,40 +99,6 @@ class TestElectriqCD20ProDehumidifier(TuyaDeviceTestCase):
             await self.subject.async_set_mode("Air clean")
             self.subject._device.anticipate_property_value.assert_not_called()
 
-    def test_light_supported_color_modes(self):
-        self.assertCountEqual(
-            self.light.supported_color_modes,
-            [COLOR_MODE_ONOFF],
-        )
-
-    def test_light_color_mode(self):
-        self.assertEqual(self.light.color_mode, COLOR_MODE_ONOFF)
-
-    def test_light_is_on(self):
-        self.dps[LIGHT_DPS] = True
-        self.assertTrue(self.light.is_on)
-
-        self.dps[LIGHT_DPS] = False
-        self.assertFalse(self.light.is_on)
-
-    async def test_light_turn_on(self):
-        async with assert_device_properties_set(self.light._device, {LIGHT_DPS: True}):
-            await self.light.async_turn_on()
-
-    async def test_light_turn_off(self):
-        async with assert_device_properties_set(self.light._device, {LIGHT_DPS: False}):
-            await self.light.async_turn_off()
-
-    async def test_toggle_turns_the_light_on_when_it_was_off(self):
-        self.dps[LIGHT_DPS] = False
-        async with assert_device_properties_set(self.light._device, {LIGHT_DPS: True}):
-            await self.light.async_toggle()
-
-    async def test_toggle_turns_the_light_off_when_it_was_on(self):
-        self.dps[LIGHT_DPS] = True
-        async with assert_device_properties_set(self.light._device, {LIGHT_DPS: False}):
-            await self.light.async_toggle()
-
     def test_state_attributes(self):
         self.dps[CURRENTHUMID_DPS] = 50
         self.dps[CURRENTTEMP_DPS] = 21
@@ -159,4 +106,3 @@ class TestElectriqCD20ProDehumidifier(TuyaDeviceTestCase):
             self.subject.device_state_attributes,
             {"current_humidity": 50, "current_temperature": 21},
         )
-        self.assertEqual(self.light.device_state_attributes, {})

+ 15 - 93
tests/devices/test_electriq_cd20_dehumidifier.py

@@ -4,7 +4,12 @@ from homeassistant.components.light import COLOR_MODE_ONOFF
 
 from ..const import ELECTRIQ_CD20PRO_DEHUMIDIFIER_PAYLOAD
 from ..helpers import assert_device_properties_set
-from .base_device_tests import TuyaDeviceTestCase
+from .base_device_tests import (
+    BasicLightTests,
+    BasicSwitchTests,
+    SwitchableTests,
+    TuyaDeviceTestCase,
+)
 
 SWITCH_DPS = "1"
 MODE_DPS = "2"
@@ -17,7 +22,9 @@ PRESET_DPS = "102"
 CURRENTTEMP_DPS = "103"
 
 
-class TestElectriqCD20ProDehumidifier(TuyaDeviceTestCase):
+class TestElectriqCD20ProDehumidifier(
+    BasicLightTests, BasicSwitchTests, SwitchableTests, TuyaDeviceTestCase
+):
     __test__ = True
 
     def setUp(self):
@@ -26,8 +33,9 @@ class TestElectriqCD20ProDehumidifier(TuyaDeviceTestCase):
         )
         self.subject = self.entities.get("humidifier")
         self.fan = self.entities.get("fan")
-        self.light = self.entities.get("light_display")
-        self.switch = self.entities.get("switch_uv_sterilization")
+        self.setUpSwitchable(SWITCH_DPS, self.subject)
+        self.setUpBasicLight(LIGHT_DPS, self.entities.get("light_display"))
+        self.setUpBasicSwitch(UV_DPS, self.entities.get("switch_uv_sterilization"))
 
     def test_supported_features(self):
         self.assertEqual(self.subject.supported_features, SUPPORT_MODES)
@@ -51,11 +59,11 @@ class TestElectriqCD20ProDehumidifier(TuyaDeviceTestCase):
         self.dps[MODE_DPS] = "high"
         self.assertEqual(self.subject.icon, "mdi:tshirt-crew-outline")
 
-        self.assertEqual(self.switch.icon, "mdi:solar-power")
+        self.assertEqual(self.basicSwitch.icon, "mdi:solar-power")
         self.dps[LIGHT_DPS] = True
-        self.assertEqual(self.light.icon, "mdi:led-on")
+        self.assertEqual(self.basicLight.icon, "mdi:led-on")
         self.dps[LIGHT_DPS] = False
-        self.assertEqual(self.light.icon, "mdi:led-off")
+        self.assertEqual(self.basicLight.icon, "mdi:led-off")
 
     def test_min_target_humidity(self):
         self.assertEqual(self.subject.min_humidity, 35)
@@ -81,31 +89,6 @@ class TestElectriqCD20ProDehumidifier(TuyaDeviceTestCase):
         ):
             await self.subject.async_set_humidity(52)
 
-    def test_is_on(self):
-        self.dps[SWITCH_DPS] = True
-        self.assertTrue(self.subject.is_on)
-        self.assertTrue(self.fan.is_on)
-
-        self.dps[SWITCH_DPS] = False
-        self.assertFalse(self.subject.is_on)
-        self.assertFalse(self.fan.is_on)
-
-        self.dps[SWITCH_DPS] = None
-        self.assertIsNone(self.subject.is_on)
-        self.assertIsNone(self.fan.is_on)
-
-    async def test_turn_on(self):
-        async with assert_device_properties_set(
-            self.subject._device, {SWITCH_DPS: True}
-        ):
-            await self.subject.async_turn_on()
-
-    async def test_turn_off(self):
-        async with assert_device_properties_set(
-            self.subject._device, {SWITCH_DPS: False}
-        ):
-            await self.subject.async_turn_off()
-
     async def test_fan_turn_on(self):
         async with assert_device_properties_set(
             self.subject._device, {SWITCH_DPS: True}
@@ -208,65 +191,6 @@ class TestElectriqCD20ProDehumidifier(TuyaDeviceTestCase):
             await self.fan.async_set_preset_mode("Oscillate")
             self.subject._device.anticipate_property_value.assert_not_called()
 
-    def test_light_supported_color_modes(self):
-        self.assertCountEqual(
-            self.light.supported_color_modes,
-            [COLOR_MODE_ONOFF],
-        )
-
-    def test_light_color_mode(self):
-        self.assertEqual(self.light.color_mode, COLOR_MODE_ONOFF)
-
-    def test_light_is_on(self):
-        self.dps[LIGHT_DPS] = True
-        self.assertTrue(self.light.is_on)
-
-        self.dps[LIGHT_DPS] = False
-        self.assertFalse(self.light.is_on)
-
-    async def test_light_turn_on(self):
-        async with assert_device_properties_set(self.light._device, {LIGHT_DPS: True}):
-            await self.light.async_turn_on()
-
-    async def test_light_turn_off(self):
-        async with assert_device_properties_set(self.light._device, {LIGHT_DPS: False}):
-            await self.light.async_turn_off()
-
-    async def test_toggle_turns_the_light_on_when_it_was_off(self):
-        self.dps[LIGHT_DPS] = False
-        async with assert_device_properties_set(self.light._device, {LIGHT_DPS: True}):
-            await self.light.async_toggle()
-
-    async def test_toggle_turns_the_light_off_when_it_was_on(self):
-        self.dps[LIGHT_DPS] = True
-        async with assert_device_properties_set(self.light._device, {LIGHT_DPS: False}):
-            await self.light.async_toggle()
-
-    def test_switch_is_on(self):
-        self.dps[UV_DPS] = True
-        self.assertTrue(self.switch.is_on)
-
-        self.dps[UV_DPS] = False
-        self.assertFalse(self.switch.is_on)
-
-    async def test_switch_turn_on(self):
-        async with assert_device_properties_set(self.switch._device, {UV_DPS: True}):
-            await self.switch.async_turn_on()
-
-    async def test_switch_turn_off(self):
-        async with assert_device_properties_set(self.switch._device, {UV_DPS: False}):
-            await self.switch.async_turn_off()
-
-    async def test_toggle_turns_the_switch_on_when_it_was_off(self):
-        self.dps[UV_DPS] = False
-        async with assert_device_properties_set(self.switch._device, {UV_DPS: True}):
-            await self.switch.async_toggle()
-
-    async def test_toggle_turns_the_switch_off_when_it_was_on(self):
-        self.dps[UV_DPS] = True
-        async with assert_device_properties_set(self.switch._device, {UV_DPS: False}):
-            await self.switch.async_toggle()
-
     def test_state_attributes(self):
         self.dps[CURRENTHUMID_DPS] = 50
         self.dps[CURRENTTEMP_DPS] = 21
@@ -276,5 +200,3 @@ class TestElectriqCD20ProDehumidifier(TuyaDeviceTestCase):
             {"current_humidity": 50, "current_temperature": 21, "anion": True},
         )
         self.assertEqual(self.fan.device_state_attributes, {})
-        self.assertEqual(self.light.device_state_attributes, {})
-        self.assertEqual(self.switch.device_state_attributes, {})

+ 20 - 120
tests/devices/test_electriq_cd25_dehumidifier.py

@@ -4,7 +4,13 @@ from homeassistant.components.light import COLOR_MODE_ONOFF
 
 from ..const import ELECTRIQ_DEHUMIDIFIER_PAYLOAD
 from ..helpers import assert_device_properties_set
-from .base_device_tests import TuyaDeviceTestCase
+from .base_device_tests import (
+    BasicLightTests,
+    BasicLockTests,
+    BasicSwitchTests,
+    SwitchableTests,
+    TuyaDeviceTestCase,
+)
 
 SWITCH_DPS = "1"
 MODE_DPS = "2"
@@ -17,7 +23,13 @@ CURRENTTEMP_DPS = "103"
 IONIZER_DPS = "104"
 
 
-class TestElectriqCD25ProDehumidifier(TuyaDeviceTestCase):
+class TestElectriqCD25ProDehumidifier(
+    BasicLightTests,
+    BasicLockTests,
+    BasicSwitchTests,
+    SwitchableTests,
+    TuyaDeviceTestCase,
+):
     __test__ = True
 
     def setUp(self):
@@ -26,9 +38,10 @@ class TestElectriqCD25ProDehumidifier(TuyaDeviceTestCase):
         )
         self.subject = self.entities.get("humidifier")
         self.fan = self.entities.get("fan")
-        self.light = self.entities.get("light_uv_sterilization")
-        self.lock = self.entities.get("lock_child_lock")
-        self.switch = self.entities.get("switch_ionizer")
+        self.setUpSwitchable(SWITCH_DPS, self.subject)
+        self.setUpBasicLight(LIGHT_DPS, self.entities.get("light_uv_sterilization"))
+        self.setUpBasicLock(LOCK_DPS, self.entities.get("lock_child_lock"))
+        self.setUpBasicSwitch(IONIZER_DPS, self.entities.get("switch_ionizer"))
 
     def test_supported_features(self):
         self.assertEqual(self.subject.supported_features, SUPPORT_MODES)
@@ -52,8 +65,8 @@ class TestElectriqCD25ProDehumidifier(TuyaDeviceTestCase):
         self.dps[MODE_DPS] = "high"
         self.assertEqual(self.subject.icon, "mdi:tshirt-crew-outline")
 
-        self.assertEqual(self.light.icon, "mdi:solar-power")
-        self.assertEqual(self.switch.icon, "mdi:creation")
+        self.assertEqual(self.basicLight.icon, "mdi:solar-power")
+        self.assertEqual(self.basicSwitch.icon, "mdi:creation")
 
     def test_min_target_humidity(self):
         self.assertEqual(self.subject.min_humidity, 35)
@@ -79,31 +92,6 @@ class TestElectriqCD25ProDehumidifier(TuyaDeviceTestCase):
         ):
             await self.subject.async_set_humidity(52)
 
-    def test_is_on(self):
-        self.dps[SWITCH_DPS] = True
-        self.assertTrue(self.subject.is_on)
-        self.assertTrue(self.fan.is_on)
-
-        self.dps[SWITCH_DPS] = False
-        self.assertFalse(self.subject.is_on)
-        self.assertFalse(self.fan.is_on)
-
-        self.dps[SWITCH_DPS] = None
-        self.assertIsNone(self.subject.is_on)
-        self.assertIsNone(self.fan.is_on)
-
-    async def test_turn_on(self):
-        async with assert_device_properties_set(
-            self.subject._device, {SWITCH_DPS: True}
-        ):
-            await self.subject.async_turn_on()
-
-    async def test_turn_off(self):
-        async with assert_device_properties_set(
-            self.subject._device, {SWITCH_DPS: False}
-        ):
-            await self.subject.async_turn_off()
-
     async def test_fan_turn_on(self):
         async with assert_device_properties_set(
             self.subject._device, {SWITCH_DPS: True}
@@ -206,91 +194,6 @@ class TestElectriqCD25ProDehumidifier(TuyaDeviceTestCase):
             await self.fan.async_set_preset_mode("Oscillate")
             self.subject._device.anticipate_property_value.assert_not_called()
 
-    def test_lock_is_locked(self):
-        self.dps[LOCK_DPS] = True
-        self.assertTrue(self.lock.is_locked)
-
-        self.dps[LOCK_DPS] = False
-        self.assertFalse(self.lock.is_locked)
-
-        self.dps[LOCK_DPS] = None
-        self.assertFalse(self.lock.is_locked)
-
-    async def test_lock_locks(self):
-        async with assert_device_properties_set(self.lock._device, {LOCK_DPS: True}):
-            await self.lock.async_lock()
-
-    async def test_lock_unlocks(self):
-        async with assert_device_properties_set(self.lock._device, {LOCK_DPS: False}):
-            await self.lock.async_unlock()
-
-    def test_light_supported_color_modes(self):
-        self.assertCountEqual(
-            self.light.supported_color_modes,
-            [COLOR_MODE_ONOFF],
-        )
-
-    def test_light_color_mode(self):
-        self.assertEqual(self.light.color_mode, COLOR_MODE_ONOFF)
-
-    def test_light_is_on(self):
-        self.dps[LIGHT_DPS] = True
-        self.assertTrue(self.light.is_on)
-
-        self.dps[LIGHT_DPS] = False
-        self.assertFalse(self.light.is_on)
-
-    async def test_light_turn_on(self):
-        async with assert_device_properties_set(self.light._device, {LIGHT_DPS: True}):
-            await self.light.async_turn_on()
-
-    async def test_light_turn_off(self):
-        async with assert_device_properties_set(self.light._device, {LIGHT_DPS: False}):
-            await self.light.async_turn_off()
-
-    async def test_toggle_turns_the_light_on_when_it_was_off(self):
-        self.dps[LIGHT_DPS] = False
-        async with assert_device_properties_set(self.light._device, {LIGHT_DPS: True}):
-            await self.light.async_toggle()
-
-    async def test_toggle_turns_the_light_off_when_it_was_on(self):
-        self.dps[LIGHT_DPS] = True
-        async with assert_device_properties_set(self.light._device, {LIGHT_DPS: False}):
-            await self.light.async_toggle()
-
-    def test_switch_is_on(self):
-        self.dps[IONIZER_DPS] = True
-        self.assertTrue(self.switch.is_on)
-
-        self.dps[IONIZER_DPS] = False
-        self.assertFalse(self.switch.is_on)
-
-    async def test_switch_turn_on(self):
-        async with assert_device_properties_set(
-            self.switch._device, {IONIZER_DPS: True}
-        ):
-            await self.switch.async_turn_on()
-
-    async def test_switch_turn_off(self):
-        async with assert_device_properties_set(
-            self.switch._device, {IONIZER_DPS: False}
-        ):
-            await self.switch.async_turn_off()
-
-    async def test_toggle_turns_the_switch_on_when_it_was_off(self):
-        self.dps[IONIZER_DPS] = False
-        async with assert_device_properties_set(
-            self.switch._device, {IONIZER_DPS: True}
-        ):
-            await self.switch.async_toggle()
-
-    async def test_toggle_turns_the_switch_off_when_it_was_on(self):
-        self.dps[IONIZER_DPS] = True
-        async with assert_device_properties_set(
-            self.switch._device, {IONIZER_DPS: False}
-        ):
-            await self.switch.async_toggle()
-
     def test_state_attributes(self):
         self.dps[CURRENTHUMID_DPS] = 50
         self.dps[CURRENTTEMP_DPS] = 21
@@ -299,6 +202,3 @@ class TestElectriqCD25ProDehumidifier(TuyaDeviceTestCase):
             {"current_humidity": 50, "current_temperature": 21},
         )
         self.assertEqual(self.fan.device_state_attributes, {})
-        self.assertEqual(self.light.device_state_attributes, {})
-        self.assertEqual(self.lock.device_state_attributes, {})
-        self.assertEqual(self.switch.device_state_attributes, {})

+ 8 - 80
tests/devices/test_electriq_desd9lw_dehumidifier.py

@@ -13,7 +13,7 @@ from homeassistant.const import STATE_UNAVAILABLE
 
 from ..const import ELECTRIQ_DESD9LW_DEHUMIDIFIER_PAYLOAD
 from ..helpers import assert_device_properties_set
-from .base_device_tests import TuyaDeviceTestCase
+from .base_device_tests import BasicLightTests, BasicSwitchTests, TuyaDeviceTestCase
 
 POWER_DPS = "1"
 HUMIDITY_DPS = "2"
@@ -27,7 +27,9 @@ LIGHT_DPS = "15"
 TEMPERATURE_DPS = "101"
 
 
-class TestElectriqDESD9LWDehumidifier(TuyaDeviceTestCase):
+class TestElectriqDESD9LWDehumidifier(
+    BasicLightTests, BasicSwitchTests, TuyaDeviceTestCase
+):
     __test__ = True
 
     def setUp(self):
@@ -36,8 +38,8 @@ class TestElectriqDESD9LWDehumidifier(TuyaDeviceTestCase):
             ELECTRIQ_DESD9LW_DEHUMIDIFIER_PAYLOAD,
         )
         self.subject = self.entities.get("climate")
-        self.light = self.entities.get("light_uv_sterilization")
-        self.switch = self.entities.get("switch_ionizer")
+        self.setUpBasicLight(LIGHT_DPS, self.entities.get("light_uv_sterilization"))
+        self.setUpBasicSwitch(SWITCH_DPS, self.entities.get("switch_ionizer"))
 
     def test_supported_features(self):
         self.assertEqual(
@@ -286,82 +288,8 @@ class TestElectriqDESD9LWDehumidifier(TuyaDeviceTestCase):
         ):
             await self.subject.async_set_swing_mode("off")
 
-    def test_light_state_attributes(self):
-        self.assertEqual(self.light.device_state_attributes, {})
-
-    def test_light_is_on(self):
-        self.dps[LIGHT_DPS] = True
-        self.assertTrue(self.light.is_on)
-        self.dps[LIGHT_DPS] = False
-        self.assertFalse(self.light.is_on)
-
-    async def test_light_turn_on(self):
-        async with assert_device_properties_set(
-            self.light._device,
-            {LIGHT_DPS: True},
-        ):
-            await self.light.async_turn_on()
-
-    async def test_light_turn_off(self):
-        async with assert_device_properties_set(
-            self.light._device,
-            {LIGHT_DPS: False},
-        ):
-            await self.light.async_turn_off()
-
-    async def test_toggle_turns_the_light_on_when_it_was_off(self):
-        self.dps[LIGHT_DPS] = False
-
-        async with assert_device_properties_set(self.light._device, {LIGHT_DPS: True}):
-            await self.light.async_toggle()
-
-    async def test_toggle_turns_the_light_off_when_it_was_on(self):
-        self.dps[LIGHT_DPS] = True
-
-        async with assert_device_properties_set(self.light._device, {LIGHT_DPS: False}):
-            await self.light.async_toggle()
-
     def test_light_icon(self):
-        self.assertEqual(self.light.icon, "mdi:solar-power")
-
-    def test_switch_state_attributes(self):
-        self.assertEqual(self.switch.device_state_attributes, {})
-
-    def test_switch_is_on(self):
-        self.dps[SWITCH_DPS] = True
-        self.assertTrue(self.switch.is_on)
-        self.dps[SWITCH_DPS] = False
-        self.assertFalse(self.switch.is_on)
-
-    async def test_switch_turn_on(self):
-        async with assert_device_properties_set(
-            self.switch._device,
-            {SWITCH_DPS: True},
-        ):
-            await self.switch.async_turn_on()
-
-    async def test_switch_turn_off(self):
-        async with assert_device_properties_set(
-            self.switch._device,
-            {SWITCH_DPS: False},
-        ):
-            await self.switch.async_turn_off()
-
-    async def test_toggle_turns_the_switch_on_when_it_was_off(self):
-        self.dps[SWITCH_DPS] = False
-
-        async with assert_device_properties_set(
-            self.switch._device, {SWITCH_DPS: True}
-        ):
-            await self.switch.async_toggle()
-
-    async def test_toggle_turns_the_switch_off_when_it_was_on(self):
-        self.dps[SWITCH_DPS] = True
-
-        async with assert_device_properties_set(
-            self.switch._device, {SWITCH_DPS: False}
-        ):
-            await self.switch.async_toggle()
+        self.assertEqual(self.basicLight.icon, "mdi:solar-power")
 
     def test_switch_icon(self):
-        self.assertEqual(self.switch.icon, "mdi:atom-variant")
+        self.assertEqual(self.basicSwitch.icon, "mdi:atom-variant")

+ 21 - 94
tests/devices/test_goldair_dehumidifier.py

@@ -21,7 +21,12 @@ from homeassistant.const import (
 
 from ..const import DEHUMIDIFIER_PAYLOAD
 from ..helpers import assert_device_properties_set
-from .base_device_tests import TuyaDeviceTestCase
+from .base_device_tests import (
+    BasicLockTests,
+    BasicSwitchTests,
+    SwitchableTests,
+    TuyaDeviceTestCase,
+)
 
 HVACMODE_DPS = "1"
 PRESET_DPS = "2"
@@ -45,17 +50,24 @@ PRESET_DRY_CLOTHES = "3"
 ERROR_TANK = "Tank full or missing"
 
 
-class TestGoldairDehumidifier(TuyaDeviceTestCase):
+class TestGoldairDehumidifier(
+    BasicLockTests,
+    BasicSwitchTests,
+    SwitchableTests,
+    TuyaDeviceTestCase,
+):
     __test__ = True
 
     def setUp(self):
         self.setUpForConfig("goldair_dehumidifier.yaml", DEHUMIDIFIER_PAYLOAD)
         self.subject = self.entities.get("humidifier")
+        self.setUpSwitchable(HVACMODE_DPS, self.subject)
         self.fan = self.entities.get("fan")
         self.climate = self.entities.get("climate_dehumidifier_as_climate")
+        # BasicLightTests mixin is not used here because the switch is inverted
         self.light = self.entities.get("light_display")
-        self.lock = self.entities.get("lock_child_lock")
-        self.switch = self.entities.get("switch_air_clean")
+        self.setUpBasicLock(LOCK_DPS, self.entities.get("lock_child_lock"))
+        self.setUpBasicSwitch(AIRCLEAN_DPS, self.entities.get("switch_air_clean"))
         self.temperature = self.entities.get("sensor_current_temperature")
         self.humidity = self.entities.get("sensor_current_humidity")
 
@@ -251,7 +263,7 @@ class TestGoldairDehumidifier(TuyaDeviceTestCase):
         self.assertEqual(self.climate.current_temperature, 25)
         self.assertEqual(self.temperature.native_value, 25)
 
-    def test_hvac_mode(self):
+    def test_climate_hvac_mode(self):
         self.dps[HVACMODE_DPS] = True
         self.assertEqual(self.climate.hvac_mode, HVAC_MODE_DRY)
 
@@ -261,41 +273,22 @@ class TestGoldairDehumidifier(TuyaDeviceTestCase):
         self.dps[HVACMODE_DPS] = None
         self.assertEqual(self.climate.hvac_mode, STATE_UNAVAILABLE)
 
-    def test_hvac_modes(self):
+    def test_climate_hvac_modes(self):
         self.assertCountEqual(self.climate.hvac_modes, [HVAC_MODE_OFF, HVAC_MODE_DRY])
 
-    async def test_turn_on(self):
+    async def test_climate_set_hvac_mode_to_dry(self):
         async with assert_device_properties_set(
             self.climate._device, {HVACMODE_DPS: True}
         ):
             await self.climate.async_set_hvac_mode(HVAC_MODE_DRY)
 
-    async def test_turn_off(self):
+    async def test_climate_set_hvac_mode_to_off(self):
 
         async with assert_device_properties_set(
             self.climate._device, {HVACMODE_DPS: False}
         ):
             await self.climate.async_set_hvac_mode(HVAC_MODE_OFF)
 
-    def test_humidifier_is_on(self):
-        self.dps[HVACMODE_DPS] = True
-        self.assertTrue(self.subject.is_on)
-
-        self.dps[HVACMODE_DPS] = False
-        self.assertFalse(self.subject.is_on)
-
-    async def test_dehumidifier_turn_on(self):
-        async with assert_device_properties_set(
-            self.subject._device, {HVACMODE_DPS: True}
-        ):
-            await self.subject.async_turn_on()
-
-    async def test_dehumidifier_turn_off(self):
-        async with assert_device_properties_set(
-            self.subject._device, {HVACMODE_DPS: False}
-        ):
-            await self.subject.async_turn_off()
-
     def test_preset_mode(self):
         self.dps[PRESET_DPS] = PRESET_NORMAL
         self.assertEqual(self.climate.preset_mode, "Normal")
@@ -569,34 +562,6 @@ class TestGoldairDehumidifier(TuyaDeviceTestCase):
             },
         )
 
-    def test_lock_state(self):
-        self.dps[LOCK_DPS] = True
-        self.assertEqual(self.lock.state, STATE_LOCKED)
-
-        self.dps[LOCK_DPS] = False
-        self.assertEqual(self.lock.state, STATE_UNLOCKED)
-
-        self.dps[LOCK_DPS] = None
-        self.assertEqual(self.lock.state, STATE_UNAVAILABLE)
-
-    def test_lock_is_locked(self):
-        self.dps[LOCK_DPS] = True
-        self.assertTrue(self.lock.is_locked)
-
-        self.dps[LOCK_DPS] = False
-        self.assertFalse(self.lock.is_locked)
-
-        self.dps[LOCK_DPS] = None
-        self.assertFalse(self.lock.is_locked)
-
-    async def test_lock_locks(self):
-        async with assert_device_properties_set(self.lock._device, {LOCK_DPS: True}):
-            await self.lock.async_lock()
-
-    async def test_lock_unlocks(self):
-        async with assert_device_properties_set(self.lock._device, {LOCK_DPS: False}):
-            await self.lock.async_unlock()
-
     def test_light_supported_color_modes(self):
         self.assertCountEqual(
             self.light.supported_color_modes,
@@ -652,45 +617,7 @@ class TestGoldairDehumidifier(TuyaDeviceTestCase):
             await self.light.async_toggle()
 
     def test_switch_icon(self):
-        self.assertEqual(self.switch.icon, "mdi:air-purifier")
-
-    def test_switch_is_on(self):
-        self.dps[AIRCLEAN_DPS] = True
-        self.assertEqual(self.switch.is_on, True)
-
-        self.dps[AIRCLEAN_DPS] = False
-        self.assertEqual(self.switch.is_on, False)
-
-    def test_switch_state_attributes(self):
-        self.assertEqual(self.switch.device_state_attributes, {})
-
-    async def test_switch_turn_on(self):
-        async with assert_device_properties_set(
-            self.switch._device, {AIRCLEAN_DPS: True}
-        ):
-            await self.switch.async_turn_on()
-
-    async def test_switch_turn_off(self):
-        async with assert_device_properties_set(
-            self.switch._device, {AIRCLEAN_DPS: False}
-        ):
-            await self.switch.async_turn_off()
-
-    async def test_toggle_turns_the_switch_on_when_it_was_off(self):
-        self.dps[AIRCLEAN_DPS] = False
-
-        async with assert_device_properties_set(
-            self.switch._device, {AIRCLEAN_DPS: True}
-        ):
-            await self.switch.async_toggle()
-
-    async def test_toggle_turns_the_switch_off_when_it_was_on(self):
-        self.dps[AIRCLEAN_DPS] = True
-
-        async with assert_device_properties_set(
-            self.switch._device, {AIRCLEAN_DPS: False}
-        ):
-            await self.switch.async_toggle()
+        self.assertEqual(self.basicSwitch.icon, "mdi:air-purifier")
 
     def test_sensor_state_class(self):
         self.assertEqual(self.temperature.state_class, "measurement")

+ 6 - 44
tests/devices/test_goldair_fan.py

@@ -20,7 +20,7 @@ from homeassistant.const import STATE_UNAVAILABLE
 
 from ..const import FAN_PAYLOAD
 from ..helpers import assert_device_properties_set
-from .base_device_tests import TuyaDeviceTestCase
+from .base_device_tests import BasicLightTests, SwitchableTests, TuyaDeviceTestCase
 
 HVACMODE_DPS = "1"
 FANMODE_DPS = "2"
@@ -30,14 +30,15 @@ TIMER_DPS = "11"
 LIGHT_DPS = "101"
 
 
-class TestGoldairFan(TuyaDeviceTestCase):
+class TestGoldairFan(BasicLightTests, SwitchableTests, TuyaDeviceTestCase):
     __test__ = True
 
     def setUp(self):
         self.setUpForConfig("goldair_fan.yaml", FAN_PAYLOAD)
         self.subject = self.entities.get("fan")
         self.climate = self.entities.get("climate")
-        self.light = self.entities.get("light_display")
+        self.setUpSwitchable(HVACMODE_DPS, self.subject)
+        self.setUpBasicLight(LIGHT_DPS, self.entities.get("light_display"))
 
     def test_supported_features(self):
         self.assertEqual(
@@ -320,48 +321,9 @@ class TestGoldairFan(TuyaDeviceTestCase):
         self.assertEqual(self.climate.device_state_attributes, {"timer": "5"})
         self.assertEqual(self.subject.device_state_attributes, {"timer": "5"})
 
-    def test_light_supported_color_modes(self):
-        self.assertCountEqual(
-            self.light.supported_color_modes,
-            [COLOR_MODE_ONOFF],
-        )
-
-    def test_light_color_mode(self):
-        self.assertEqual(self.light.color_mode, COLOR_MODE_ONOFF)
-
     def test_light_icon(self):
         self.dps[LIGHT_DPS] = True
-        self.assertEqual(self.light.icon, "mdi:led-on")
+        self.assertEqual(self.basicLight.icon, "mdi:led-on")
 
         self.dps[LIGHT_DPS] = False
-        self.assertEqual(self.light.icon, "mdi:led-off")
-
-    def test_light_is_on(self):
-        self.dps[LIGHT_DPS] = True
-        self.assertEqual(self.light.is_on, True)
-
-        self.dps[LIGHT_DPS] = False
-        self.assertEqual(self.light.is_on, False)
-
-    def test_light_state_attributes(self):
-        self.assertEqual(self.light.device_state_attributes, {})
-
-    async def test_light_turn_on(self):
-        async with assert_device_properties_set(self.light._device, {LIGHT_DPS: True}):
-            await self.light.async_turn_on()
-
-    async def test_light_turn_off(self):
-        async with assert_device_properties_set(self.light._device, {LIGHT_DPS: False}):
-            await self.light.async_turn_off()
-
-    async def test_toggle_turns_the_light_on_when_it_was_off(self):
-        self.dps[LIGHT_DPS] = False
-
-        async with assert_device_properties_set(self.light._device, {LIGHT_DPS: True}):
-            await self.light.async_toggle()
-
-    async def test_toggle_turns_the_light_off_when_it_was_on(self):
-        self.dps[LIGHT_DPS] = True
-
-        async with assert_device_properties_set(self.light._device, {LIGHT_DPS: False}):
-            await self.light.async_toggle()
+        self.assertEqual(self.basicLight.icon, "mdi:led-off")

+ 3 - 31
tests/devices/test_goldair_geco_heater.py

@@ -8,7 +8,7 @@ from homeassistant.const import STATE_UNAVAILABLE
 
 from ..const import GECO_HEATER_PAYLOAD
 from ..helpers import assert_device_properties_set
-from .base_device_tests import TuyaDeviceTestCase
+from .base_device_tests import BasicLockTests, TuyaDeviceTestCase
 
 HVACMODE_DPS = "1"
 LOCK_DPS = "2"
@@ -18,13 +18,13 @@ TIMER_DPS = "5"
 ERROR_DPS = "6"
 
 
-class TestGoldairGECOHeater(TuyaDeviceTestCase):
+class TestGoldairGECOHeater(BasicLockTests, TuyaDeviceTestCase):
     __test__ = True
 
     def setUp(self):
         self.setUpForConfig("goldair_geco_heater.yaml", GECO_HEATER_PAYLOAD)
         self.subject = self.entities.get("climate")
-        self.lock = self.entities.get("lock_child_lock")
+        self.setUpBasicLock(LOCK_DPS, self.entities.get("lock_child_lock"))
 
     def test_supported_features(self):
         self.assertEqual(
@@ -134,31 +134,3 @@ class TestGoldairGECOHeater(TuyaDeviceTestCase):
         self.assertDictEqual(
             self.subject.device_state_attributes, {"error": "OK", "timer": 0}
         )
-
-    def test_lock_state(self):
-        self.dps[LOCK_DPS] = True
-        self.assertEqual(self.lock.state, STATE_LOCKED)
-
-        self.dps[LOCK_DPS] = False
-        self.assertEqual(self.lock.state, STATE_UNLOCKED)
-
-        self.dps[LOCK_DPS] = None
-        self.assertEqual(self.lock.state, STATE_UNAVAILABLE)
-
-    def test_lock_is_locked(self):
-        self.dps[LOCK_DPS] = True
-        self.assertTrue(self.lock.is_locked)
-
-        self.dps[LOCK_DPS] = False
-        self.assertFalse(self.lock.is_locked)
-
-        self.dps[LOCK_DPS] = None
-        self.assertFalse(self.lock.is_locked)
-
-    async def test_lock_locks(self):
-        async with assert_device_properties_set(self.lock._device, {LOCK_DPS: True}):
-            await self.lock.async_lock()
-
-    async def test_lock_unlocks(self):
-        async with assert_device_properties_set(self.lock._device, {LOCK_DPS: False}):
-            await self.lock.async_unlock()

+ 3 - 31
tests/devices/test_goldair_gpcv_heater.py

@@ -9,7 +9,7 @@ from homeassistant.const import STATE_UNAVAILABLE
 
 from ..const import GPCV_HEATER_PAYLOAD
 from ..helpers import assert_device_properties_set
-from .base_device_tests import TuyaDeviceTestCase
+from .base_device_tests import BasicLockTests, TuyaDeviceTestCase
 
 HVACMODE_DPS = "1"
 LOCK_DPS = "2"
@@ -20,13 +20,13 @@ ERROR_DPS = "6"
 PRESET_DPS = "7"
 
 
-class TestGoldairGPCVHeater(TuyaDeviceTestCase):
+class TestGoldairGPCVHeater(BasicLockTests, TuyaDeviceTestCase):
     __test__ = True
 
     def setUp(self):
         self.setUpForConfig("goldair_gpcv_heater.yaml", GPCV_HEATER_PAYLOAD)
         self.subject = self.entities.get("climate")
-        self.lock = self.entities.get("lock_child_lock")
+        self.setUpBasicLock(LOCK_DPS, self.entities.get("lock_child_lock"))
 
     def test_supported_features(self):
         self.assertEqual(
@@ -175,31 +175,3 @@ class TestGoldairGPCVHeater(TuyaDeviceTestCase):
         self.assertDictEqual(
             self.subject.device_state_attributes, {"error": "OK", "timer": 0}
         )
-
-    def test_lock_state(self):
-        self.dps[LOCK_DPS] = True
-        self.assertEqual(self.lock.state, STATE_LOCKED)
-
-        self.dps[LOCK_DPS] = False
-        self.assertEqual(self.lock.state, STATE_UNLOCKED)
-
-        self.dps[LOCK_DPS] = None
-        self.assertEqual(self.lock.state, STATE_UNAVAILABLE)
-
-    def test_lock_is_locked(self):
-        self.dps[LOCK_DPS] = True
-        self.assertTrue(self.lock.is_locked)
-
-        self.dps[LOCK_DPS] = False
-        self.assertFalse(self.lock.is_locked)
-
-        self.dps[LOCK_DPS] = None
-        self.assertFalse(self.lock.is_locked)
-
-    async def test_lock_locks(self):
-        async with assert_device_properties_set(self.lock._device, {LOCK_DPS: True}):
-            await self.lock.async_lock()
-
-    async def test_lock_unlocks(self):
-        async with assert_device_properties_set(self.lock._device, {LOCK_DPS: False}):
-            await self.lock.async_unlock()

+ 6 - 82
tests/devices/test_goldair_gpph_heater.py

@@ -7,14 +7,12 @@ from homeassistant.components.climate.const import (
     SUPPORT_SWING_MODE,
     SUPPORT_TARGET_TEMPERATURE,
 )
-from homeassistant.components.light import COLOR_MODE_ONOFF
-from homeassistant.components.lock import STATE_LOCKED, STATE_UNLOCKED
 
 from homeassistant.const import STATE_UNAVAILABLE
 
 from ..const import GPPH_HEATER_PAYLOAD
 from ..helpers import assert_device_properties_set
-from .base_device_tests import TuyaDeviceTestCase
+from .base_device_tests import BasicLightTests, BasicLockTests, TuyaDeviceTestCase
 
 HVACMODE_DPS = "1"
 TEMPERATURE_DPS = "2"
@@ -30,14 +28,14 @@ SWING_DPS = "105"
 ECOTEMP_DPS = "106"
 
 
-class TestGoldairHeater(TuyaDeviceTestCase):
+class TestGoldairHeater(BasicLightTests, BasicLockTests, TuyaDeviceTestCase):
     __test__ = True
 
     def setUp(self):
         self.setUpForConfig("goldair_gpph_heater.yaml", GPPH_HEATER_PAYLOAD)
         self.subject = self.entities.get("climate")
-        self.light = self.entities.get("light_display")
-        self.lock = self.entities.get("lock_child_lock")
+        self.setUpBasicLight(LIGHT_DPS, self.entities.get("light_display"))
+        self.setUpBasicLock(LOCK_DPS, self.entities.get("lock_child_lock"))
         self.timer = self.entities.get("number_timer")
 
     def test_supported_features(self):
@@ -330,86 +328,12 @@ class TestGoldairHeater(TuyaDeviceTestCase):
             },
         )
 
-    def test_lock_state(self):
-        self.dps[LOCK_DPS] = True
-        self.assertEqual(self.lock.state, STATE_LOCKED)
-
-        self.dps[LOCK_DPS] = False
-        self.assertEqual(self.lock.state, STATE_UNLOCKED)
-
-        self.dps[LOCK_DPS] = None
-        self.assertEqual(self.lock.state, STATE_UNAVAILABLE)
-
-    def test_lock_is_locked(self):
-        self.dps[LOCK_DPS] = True
-        self.assertTrue(self.lock.is_locked)
-
-        self.dps[LOCK_DPS] = False
-        self.assertFalse(self.lock.is_locked)
-
-        self.dps[LOCK_DPS] = None
-        self.assertFalse(self.lock.is_locked)
-
-    async def test_lock_locks(self):
-        async with assert_device_properties_set(self.lock._device, {LOCK_DPS: True}):
-            await self.lock.async_lock()
-
-    async def test_lock_unlocks(self):
-        async with assert_device_properties_set(self.lock._device, {LOCK_DPS: False}):
-            await self.lock.async_unlock()
-
-    def test_light_supported_color_modes(self):
-        self.assertCountEqual(
-            self.light.supported_color_modes,
-            [COLOR_MODE_ONOFF],
-        )
-
-    def test_light_color_mode(self):
-        self.assertEqual(self.light.color_mode, COLOR_MODE_ONOFF)
-
-    def test_light_has_no_brightness(self):
-        self.assertIsNone(self.light.brightness)
-
-    def test_light_has_no_effects(self):
-        self.assertIsNone(self.light.effect_list)
-        self.assertIsNone(self.light.effect)
-
     def test_light_icon(self):
         self.dps[LIGHT_DPS] = True
-        self.assertEqual(self.light.icon, "mdi:led-on")
+        self.assertEqual(self.basicLight.icon, "mdi:led-on")
 
         self.dps[LIGHT_DPS] = False
-        self.assertEqual(self.light.icon, "mdi:led-off")
-
-    def test_light_is_on(self):
-        self.dps[LIGHT_DPS] = True
-        self.assertEqual(self.light.is_on, True)
-
-        self.dps[LIGHT_DPS] = False
-        self.assertEqual(self.light.is_on, False)
-
-    def test_light_state_attributes(self):
-        self.assertEqual(self.light.device_state_attributes, {})
-
-    async def test_light_turn_on(self):
-        async with assert_device_properties_set(self.light._device, {LIGHT_DPS: True}):
-            await self.light.async_turn_on()
-
-    async def test_light_turn_off(self):
-        async with assert_device_properties_set(self.light._device, {LIGHT_DPS: False}):
-            await self.light.async_turn_off()
-
-    async def test_toggle_turns_the_light_on_when_it_was_off(self):
-        self.dps[LIGHT_DPS] = False
-
-        async with assert_device_properties_set(self.light._device, {LIGHT_DPS: True}):
-            await self.light.async_toggle()
-
-    async def test_toggle_turns_the_light_off_when_it_was_on(self):
-        self.dps[LIGHT_DPS] = True
-
-        async with assert_device_properties_set(self.light._device, {LIGHT_DPS: False}):
-            await self.light.async_toggle()
+        self.assertEqual(self.basicLight.icon, "mdi:led-off")
 
     def test_timer_min_value(self):
         self.assertEqual(self.timer.min_value, 0)

+ 3 - 27
tests/devices/test_kogan_dehumidifier.py

@@ -3,7 +3,7 @@ from homeassistant.components.humidifier import SUPPORT_MODES
 
 from ..const import KOGAN_DEHUMIDIFIER_PAYLOAD
 from ..helpers import assert_device_properties_set
-from .base_device_tests import TuyaDeviceTestCase
+from .base_device_tests import SwitchableTests, TuyaDeviceTestCase
 
 SWITCH_DPS = "1"
 MODE_DPS = "2"
@@ -15,12 +15,13 @@ COUNTDOWN_DPS = "13"
 HUMIDITY_DPS = "101"
 
 
-class TestKoganDehumidifier(TuyaDeviceTestCase):
+class TestKoganDehumidifier(SwitchableTests, TuyaDeviceTestCase):
     __test__ = True
 
     def setUp(self):
         self.setUpForConfig("kogan_dehumidifier.yaml", KOGAN_DEHUMIDIFIER_PAYLOAD)
         self.subject = self.entities.get("humidifier")
+        self.setUpSwitchable(SWITCH_DPS, self.subject)
         self.fan = self.entities.get("fan")
 
     def test_supported_features(self):
@@ -58,31 +59,6 @@ class TestKoganDehumidifier(TuyaDeviceTestCase):
         self.dps[HUMIDITY_DPS] = 55
         self.assertEqual(self.subject.target_humidity, 55)
 
-    def test_is_on(self):
-        self.dps[SWITCH_DPS] = True
-        self.assertTrue(self.subject.is_on)
-        self.assertTrue(self.fan.is_on)
-
-        self.dps[SWITCH_DPS] = False
-        self.assertFalse(self.subject.is_on)
-        self.assertFalse(self.fan.is_on)
-
-        self.dps[SWITCH_DPS] = None
-        self.assertIsNone(self.subject.is_on)
-        self.assertIsNone(self.fan.is_on)
-
-    async def test_turn_on(self):
-        async with assert_device_properties_set(
-            self.subject._device, {SWITCH_DPS: True}
-        ):
-            await self.subject.async_turn_on()
-
-    async def test_turn_off(self):
-        async with assert_device_properties_set(
-            self.subject._device, {SWITCH_DPS: False}
-        ):
-            await self.subject.async_turn_off()
-
     async def test_fan_turn_on(self):
         async with assert_device_properties_set(
             self.subject._device, {SWITCH_DPS: True}

+ 3 - 31
tests/devices/test_kogan_kahtp_heater.py

@@ -9,7 +9,7 @@ from homeassistant.const import STATE_UNAVAILABLE
 
 from ..const import KOGAN_HEATER_PAYLOAD
 from ..helpers import assert_device_properties_set
-from .base_device_tests import TuyaDeviceTestCase
+from .base_device_tests import BasicLockTests, TuyaDeviceTestCase
 
 TEMPERATURE_DPS = "2"
 CURRENTTEMP_DPS = "3"
@@ -19,13 +19,13 @@ HVACMODE_DPS = "7"
 TIMER_DPS = "8"
 
 
-class TestGoldairKoganKAHTPHeater(TuyaDeviceTestCase):
+class TestGoldairKoganKAHTPHeater(BasicLockTests, TuyaDeviceTestCase):
     __test__ = True
 
     def setUp(self):
         self.setUpForConfig("kogan_kahtp_heater.yaml", KOGAN_HEATER_PAYLOAD)
         self.subject = self.entities.get("climate")
-        self.lock = self.entities.get("lock_child_lock")
+        self.setUpBasicLock(LOCK_DPS, self.entities.get("lock_child_lock"))
 
     def test_supported_features(self):
         self.assertEqual(
@@ -166,31 +166,3 @@ class TestGoldairKoganKAHTPHeater(TuyaDeviceTestCase):
             self.subject.device_state_attributes,
             {"timer": 10},
         )
-
-    def test_lock_state(self):
-        self.dps[LOCK_DPS] = True
-        self.assertEqual(self.lock.state, STATE_LOCKED)
-
-        self.dps[LOCK_DPS] = False
-        self.assertEqual(self.lock.state, STATE_UNLOCKED)
-
-        self.dps[LOCK_DPS] = None
-        self.assertEqual(self.lock.state, STATE_UNAVAILABLE)
-
-    def test_lock_is_locked(self):
-        self.dps[LOCK_DPS] = True
-        self.assertTrue(self.lock.is_locked)
-
-        self.dps[LOCK_DPS] = False
-        self.assertFalse(self.lock.is_locked)
-
-        self.dps[LOCK_DPS] = None
-        self.assertFalse(self.lock.is_locked)
-
-    async def test_lock_locks(self):
-        async with assert_device_properties_set(self.lock._device, {LOCK_DPS: True}):
-            await self.lock.async_lock()
-
-    async def test_lock_unlocks(self):
-        async with assert_device_properties_set(self.lock._device, {LOCK_DPS: False}):
-            await self.lock.async_unlock()

+ 3 - 31
tests/devices/test_kogan_kawfhtp_heater.py

@@ -9,7 +9,7 @@ from homeassistant.const import STATE_UNAVAILABLE
 
 from ..const import KOGAN_KAWFHTP_HEATER_PAYLOAD
 from ..helpers import assert_device_properties_set
-from .base_device_tests import TuyaDeviceTestCase
+from .base_device_tests import BasicLockTests, TuyaDeviceTestCase
 
 HVACMODE_DPS = "1"
 TEMPERATURE_DPS = "3"
@@ -19,13 +19,13 @@ PRESET_DPS = "7"
 LOCK_DPS = "2"
 
 
-class TestGoldairKoganKAHTPHeater(TuyaDeviceTestCase):
+class TestGoldairKoganKAHTPHeater(BasicLockTests, TuyaDeviceTestCase):
     __test__ = True
 
     def setUp(self):
         self.setUpForConfig("kogan_kawfhtp_heater.yaml", KOGAN_KAWFHTP_HEATER_PAYLOAD)
         self.subject = self.entities.get("climate")
-        self.lock = self.entities.get("lock_child_lock")
+        self.setUpBasicLock(LOCK_DPS, self.entities.get("lock_child_lock"))
 
     def test_supported_features(self):
         self.assertEqual(
@@ -163,31 +163,3 @@ class TestGoldairKoganKAHTPHeater(TuyaDeviceTestCase):
     def test_state_attributes(self):
         self.dps[TIMER_DPS] = 10
         self.assertDictEqual(self.subject.device_state_attributes, {"timer": 10})
-
-    def test_lock_state(self):
-        self.dps[LOCK_DPS] = True
-        self.assertEqual(self.lock.state, STATE_LOCKED)
-
-        self.dps[LOCK_DPS] = False
-        self.assertEqual(self.lock.state, STATE_UNLOCKED)
-
-        self.dps[LOCK_DPS] = None
-        self.assertEqual(self.lock.state, STATE_UNAVAILABLE)
-
-    def test_lock_is_locked(self):
-        self.dps[LOCK_DPS] = True
-        self.assertTrue(self.lock.is_locked)
-
-        self.dps[LOCK_DPS] = False
-        self.assertFalse(self.lock.is_locked)
-
-        self.dps[LOCK_DPS] = None
-        self.assertFalse(self.lock.is_locked)
-
-    async def test_lock_locks(self):
-        async with assert_device_properties_set(self.lock._device, {LOCK_DPS: True}):
-            await self.lock.async_lock()
-
-    async def test_lock_unlocks(self):
-        async with assert_device_properties_set(self.lock._device, {LOCK_DPS: False}):
-            await self.lock.async_unlock()

+ 20 - 137
tests/devices/test_lexy_f501_fan.py

@@ -3,13 +3,16 @@ from homeassistant.components.fan import (
     SUPPORT_PRESET_MODE,
     SUPPORT_SET_SPEED,
 )
-from homeassistant.components.light import COLOR_MODE_ONOFF
-from homeassistant.components.lock import STATE_LOCKED, STATE_UNLOCKED
-from homeassistant.const import STATE_UNAVAILABLE
 
 from ..const import LEXY_F501_PAYLOAD
 from ..helpers import assert_device_properties_set
-from .base_device_tests import TuyaDeviceTestCase
+from .base_device_tests import (
+    BasicLightTests,
+    BasicLockTests,
+    BasicSwitchTests,
+    SwitchableTests,
+    TuyaDeviceTestCase,
+)
 
 POWER_DPS = "1"
 PRESET_DPS = "2"
@@ -21,15 +24,22 @@ SWITCH_DPS = "17"
 SPEED_DPS = "102"
 
 
-class TestLexyF501Fan(TuyaDeviceTestCase):
+class TestLexyF501Fan(
+    SwitchableTests,
+    BasicLightTests,
+    BasicLockTests,
+    BasicSwitchTests,
+    TuyaDeviceTestCase,
+):
     __test__ = True
 
     def setUp(self):
         self.setUpForConfig("lexy_f501_fan.yaml", LEXY_F501_PAYLOAD)
         self.subject = self.entities.get("fan")
-        self.light = self.entities.get("light")
-        self.lock = self.entities.get("lock_child_lock")
-        self.switch = self.entities.get("switch_sound")
+        self.setUpSwitchable(POWER_DPS, self.subject)
+        self.setUpBasicLight(LIGHT_DPS, self.entities.get("light"))
+        self.setUpBasicLock(LOCK_DPS, self.entities.get("lock_child_lock"))
+        self.setUpBasicSwitch(SWITCH_DPS, self.entities.get("switch_sound"))
 
     def test_supported_features(self):
         self.assertEqual(
@@ -37,28 +47,6 @@ class TestLexyF501Fan(TuyaDeviceTestCase):
             SUPPORT_OSCILLATE | SUPPORT_PRESET_MODE | SUPPORT_SET_SPEED,
         )
 
-    def test_is_on(self):
-        self.dps[POWER_DPS] = True
-        self.assertTrue(self.subject.is_on)
-
-        self.dps[POWER_DPS] = False
-        self.assertFalse(self.subject.is_on)
-
-        self.dps[POWER_DPS] = None
-        self.assertIsNone(self.subject.is_on)
-
-    async def test_turn_on(self):
-        async with assert_device_properties_set(
-            self.subject._device, {POWER_DPS: True}
-        ):
-            await self.subject.async_turn_on()
-
-    async def test_turn_off(self):
-        async with assert_device_properties_set(
-            self.subject._device, {POWER_DPS: False}
-        ):
-            await self.subject.async_turn_off()
-
     def test_preset_mode(self):
         self.dps[PRESET_DPS] = "forestwindhigh"
         self.assertEqual(self.subject.preset_mode, "Forest High")
@@ -154,114 +142,9 @@ class TestLexyF501Fan(TuyaDeviceTestCase):
         self.dps[TIMER_DPS] = "5"
         self.assertEqual(self.subject.device_state_attributes, {"timer": 5})
 
-    def test_light_supported_color_modes(self):
-        self.assertCountEqual(
-            self.light.supported_color_modes,
-            [COLOR_MODE_ONOFF],
-        )
-
-    def test_light_color_mode(self):
-        self.assertEqual(self.light.color_mode, COLOR_MODE_ONOFF)
-
-    def test_light_is_on(self):
-        self.dps[LIGHT_DPS] = True
-        self.assertEqual(self.light.is_on, True)
-
-        self.dps[LIGHT_DPS] = False
-        self.assertEqual(self.light.is_on, False)
-
-    def test_light_state_attributes(self):
-        self.assertEqual(self.light.device_state_attributes, {})
-
-    async def test_light_turn_on(self):
-        async with assert_device_properties_set(self.light._device, {LIGHT_DPS: True}):
-            await self.light.async_turn_on()
-
-    async def test_light_turn_off(self):
-        async with assert_device_properties_set(self.light._device, {LIGHT_DPS: False}):
-            await self.light.async_turn_off()
-
-    async def test_toggle_turns_the_light_on_when_it_was_off(self):
-        self.dps[LIGHT_DPS] = False
-
-        async with assert_device_properties_set(self.light._device, {LIGHT_DPS: True}):
-            await self.light.async_toggle()
-
-    async def test_toggle_turns_the_light_off_when_it_was_on(self):
-        self.dps[LIGHT_DPS] = True
-
-        async with assert_device_properties_set(self.light._device, {LIGHT_DPS: False}):
-            await self.light.async_toggle()
-
-    def test_switch_is_on(self):
-        self.dps[SWITCH_DPS] = True
-        self.assertEqual(self.switch.is_on, True)
-
-        self.dps[SWITCH_DPS] = False
-        self.assertEqual(self.switch.is_on, False)
-
-    def test_switch_state_attributes(self):
-        self.assertEqual(self.switch.device_state_attributes, {})
-
-    async def test_switch_turn_on(self):
-        async with assert_device_properties_set(
-            self.switch._device, {SWITCH_DPS: True}
-        ):
-            await self.switch.async_turn_on()
-
-    async def test_switch_turn_off(self):
-        async with assert_device_properties_set(
-            self.switch._device, {SWITCH_DPS: False}
-        ):
-            await self.switch.async_turn_off()
-
-    async def test_toggle_turns_the_switch_on_when_it_was_off(self):
-        self.dps[SWITCH_DPS] = False
-
-        async with assert_device_properties_set(
-            self.switch._device, {SWITCH_DPS: True}
-        ):
-            await self.switch.async_toggle()
-
-    async def test_toggle_turns_the_switch_off_when_it_was_on(self):
-        self.dps[SWITCH_DPS] = True
-
-        async with assert_device_properties_set(
-            self.switch._device, {SWITCH_DPS: False}
-        ):
-            await self.switch.async_toggle()
-
-    def test_lock_state(self):
-        self.dps[LOCK_DPS] = True
-        self.assertEqual(self.lock.state, STATE_LOCKED)
-
-        self.dps[LOCK_DPS] = False
-        self.assertEqual(self.lock.state, STATE_UNLOCKED)
-
-        self.dps[LOCK_DPS] = None
-        self.assertEqual(self.lock.state, STATE_UNAVAILABLE)
-
-    def test_lock_is_locked(self):
-        self.dps[LOCK_DPS] = True
-        self.assertTrue(self.lock.is_locked)
-
-        self.dps[LOCK_DPS] = False
-        self.assertFalse(self.lock.is_locked)
-
-        self.dps[LOCK_DPS] = None
-        self.assertFalse(self.lock.is_locked)
-
-    async def test_lock_locks(self):
-        async with assert_device_properties_set(self.lock._device, {LOCK_DPS: True}):
-            await self.lock.async_lock()
-
-    async def test_lock_unlocks(self):
-        async with assert_device_properties_set(self.lock._device, {LOCK_DPS: False}):
-            await self.lock.async_unlock()
-
     def test_icons(self):
         self.dps[LIGHT_DPS] = True
-        self.assertEqual(self.light.icon, "mdi:led-on")
+        self.assertEqual(self.basicLight.icon, "mdi:led-on")
 
         self.dps[LIGHT_DPS] = False
-        self.assertEqual(self.light.icon, "mdi:led-off")
+        self.assertEqual(self.basicLight.icon, "mdi:led-off")

+ 6 - 51
tests/devices/test_purline_m100_heater.py

@@ -17,7 +17,7 @@ from ..helpers import (
     assert_device_properties_set,
     assert_device_properties_set_optional,
 )
-from .base_device_tests import TuyaDeviceTestCase
+from .base_device_tests import BasicSwitchTests, TuyaDeviceTestCase
 
 HVACMODE_DPS = "1"
 TEMPERATURE_DPS = "2"
@@ -30,14 +30,17 @@ SWITCH_DPS = "101"
 SWING_DPS = "102"
 
 
-class TestPulineM100Heater(TuyaDeviceTestCase):
+class TestPulineM100Heater(BasicSwitchTests, TuyaDeviceTestCase):
     __test__ = True
 
     def setUp(self):
         self.setUpForConfig("purline_m100_heater.yaml", PURLINE_M100_HEATER_PAYLOAD)
         self.subject = self.entities.get("climate")
+        # BasicLightTests mixin not used due to inverted switch
         self.light = self.entities.get("light_display")
-        self.switch = self.entities.get("switch_open_window_detector")
+        self.setUpBasicSwitch(
+            SWITCH_DPS, self.entities.get("switch_open_window_detector")
+        )
 
     def test_supported_features(self):
         self.assertEqual(
@@ -260,51 +263,3 @@ class TestPulineM100Heater(TuyaDeviceTestCase):
             self.light._device, {LIGHTOFF_DPS: True}
         ):
             await self.light.async_toggle()
-
-    def test_switch_class_is_switch(self):
-        self.assertEqual(self.switch.device_class, DEVICE_CLASS_SWITCH)
-
-    def test_switch_is_on(self):
-        self.dps[SWITCH_DPS] = True
-        self.assertTrue(self.switch.is_on)
-
-        self.dps[SWITCH_DPS] = False
-        self.assertFalse(self.switch.is_on)
-
-    def test_switch_is_on_when_unavailable(self):
-        self.dps[SWITCH_DPS] = None
-        self.assertIsNone(self.switch.is_on)
-
-    async def test_switch_turn_on(self):
-        async with assert_device_properties_set(
-            self.switch._device, {SWITCH_DPS: True}
-        ):
-            await self.switch.async_turn_on()
-
-    async def test_switch_turn_off(self):
-        async with assert_device_properties_set(
-            self.switch._device, {SWITCH_DPS: False}
-        ):
-            await self.switch.async_turn_off()
-
-    async def test_toggle_turns_the_switch_on_when_it_was_off(self):
-        self.dps[SWITCH_DPS] = False
-
-        async with assert_device_properties_set(
-            self.switch._device, {SWITCH_DPS: True}
-        ):
-            await self.switch.async_toggle()
-
-    async def test_toggle_turns_the_switch_off_when_it_was_on(self):
-        self.dps[SWITCH_DPS] = True
-
-        async with assert_device_properties_set(
-            self.switch._device, {SWITCH_DPS: False}
-        ):
-            await self.switch.async_toggle()
-
-    def test_switch_returns_none_for_power(self):
-        self.assertIsNone(self.switch.current_power_w)
-
-    def test_switch_state_attributes_set(self):
-        self.assertEqual(self.switch.device_state_attributes, {})

+ 20 - 144
tests/devices/test_renpho_rp_ap001s.py

@@ -5,7 +5,13 @@ from homeassistant.const import STATE_UNAVAILABLE
 
 from ..const import RENPHO_PURIFIER_PAYLOAD
 from ..helpers import assert_device_properties_set
-from .base_device_tests import TuyaDeviceTestCase
+from .base_device_tests import (
+    BasicLightTests,
+    BasicLockTests,
+    BasicSwitchTests,
+    SwitchableTests,
+    TuyaDeviceTestCase,
+)
 
 SWITCH_DPS = "1"
 PRESET_DPS = "4"
@@ -20,41 +26,26 @@ ACTIVATED_DPS = "104"
 HEPA_DPS = "105"
 
 
-class TestRenphoPurifier(TuyaDeviceTestCase):
+class TestRenphoPurifier(
+    BasicLightTests,
+    BasicLockTests,
+    BasicSwitchTests,
+    SwitchableTests,
+    TuyaDeviceTestCase,
+):
     __test__ = True
 
     def setUp(self):
         self.setUpForConfig("renpho_rp_ap001s.yaml", RENPHO_PURIFIER_PAYLOAD)
-        self.subject = self.entities["fan"]
-        self.light = self.entities["light_aq_indicator"]
-        self.lock = self.entities["lock_child_lock"]
-        self.switch = self.entities["switch_sleep"]
+        self.subject = self.entities.get("fan")
+        self.setUpSwitchable(SWITCH_DPS, self.subject)
+        self.setUpBasicLight(LIGHT_DPS, self.entities.get("light_aq_indicator"))
+        self.setUpBasicLock(LOCK_DPS, self.entities.get("lock_child_lock"))
+        self.setUpBasicSwitch(SLEEP_DPS, self.entities.get("switch_sleep"))
 
     def test_supported_features(self):
         self.assertEqual(self.subject.supported_features, SUPPORT_PRESET_MODE)
 
-    def test_is_on(self):
-        self.dps[SWITCH_DPS] = True
-        self.assertTrue(self.subject.is_on)
-        self.dps[SWITCH_DPS] = False
-        self.assertFalse(self.subject.is_on)
-        self.dps[SWITCH_DPS] = None
-        self.assertIsNone(self.subject.is_on)
-
-    async def test_turn_on(self):
-        async with assert_device_properties_set(
-            self.subject._device,
-            {SWITCH_DPS: True},
-        ):
-            await self.subject.async_turn_on()
-
-    async def test_turn_off(self):
-        async with assert_device_properties_set(
-            self.subject._device,
-            {SWITCH_DPS: False},
-        ):
-            await self.subject.async_turn_off()
-
     def test_preset_modes(self):
         self.assertCountEqual(
             self.subject.preset_modes,
@@ -119,119 +110,4 @@ class TestRenphoPurifier(TuyaDeviceTestCase):
             },
         )
 
-    def test_lock_state(self):
-        self.dps[LOCK_DPS] = True
-        self.assertEqual(self.lock.state, STATE_LOCKED)
-
-        self.dps[LOCK_DPS] = False
-        self.assertEqual(self.lock.state, STATE_UNLOCKED)
-
-        self.dps[LOCK_DPS] = None
-        self.assertEqual(self.lock.state, STATE_UNAVAILABLE)
-
-    def test_lock_is_locked(self):
-        self.dps[LOCK_DPS] = True
-        self.assertTrue(self.lock.is_locked)
-
-        self.dps[LOCK_DPS] = False
-        self.assertFalse(self.lock.is_locked)
-
-        self.dps[LOCK_DPS] = None
-        self.assertFalse(self.lock.is_locked)
-
-    async def test_lock_locks(self):
-        async with assert_device_properties_set(
-            self.lock._device,
-            {LOCK_DPS: True},
-        ):
-            await self.lock.async_lock()
-
-    async def test_lock_unlocks(self):
-        async with assert_device_properties_set(
-            self.lock._device,
-            {LOCK_DPS: False},
-        ):
-            await self.lock.async_unlock()
-
-    def test_light_supported_color_modes(self):
-        self.assertCountEqual(
-            self.light.supported_color_modes,
-            [COLOR_MODE_ONOFF],
-        )
-
-    def test_light_color_mode(self):
-        self.assertEqual(self.light.color_mode, COLOR_MODE_ONOFF)
-
-    def test_light_has_no_brightness(self):
-        self.assertIsNone(self.light.brightness)
-
-    def test_light_icon(self):
-        self.dps[LIGHT_DPS] = True
-        self.assertEqual(self.light.icon, "mdi:led-on")
-
-        self.dps[LIGHT_DPS] = False
-        self.assertEqual(self.light.icon, "mdi:led-off")
-
-    def test_light_is_on(self):
-        self.dps[LIGHT_DPS] = True
-        self.assertEqual(self.light.is_on, True)
-
-        self.dps[LIGHT_DPS] = False
-        self.assertEqual(self.light.is_on, False)
-
-    async def test_light_turn_on(self):
-        async with assert_device_properties_set(self.light._device, {LIGHT_DPS: True}):
-            await self.light.async_turn_on()
-
-    async def test_light_turn_off(self):
-        async with assert_device_properties_set(self.light._device, {LIGHT_DPS: False}):
-            await self.light.async_turn_off()
-
-    async def test_toggle_turns_the_light_on_when_it_was_off(self):
-        self.dps[LIGHT_DPS] = False
-
-        async with assert_device_properties_set(self.light._device, {LIGHT_DPS: True}):
-            await self.light.async_toggle()
-
-    async def test_toggle_turns_the_light_off_when_it_was_on(self):
-        self.dps[LIGHT_DPS] = True
-
-        async with assert_device_properties_set(self.light._device, {LIGHT_DPS: False}):
-            await self.light.async_toggle()
-
-    def test_switch_icon(self):
-        self.assertEqual(self.switch.icon, "mdi:power-sleep")
-
-    def test_switch_is_on(self):
-        self.dps[SLEEP_DPS] = True
-        self.assertEqual(self.switch.is_on, True)
-
-        self.dps[SLEEP_DPS] = False
-        self.assertEqual(self.switch.is_on, False)
-
-    def test_switch_state_attributes(self):
-        self.assertEqual(self.switch.device_state_attributes, {})
-
-    async def test_switch_turn_on(self):
-        async with assert_device_properties_set(self.switch._device, {SLEEP_DPS: True}):
-            await self.switch.async_turn_on()
-
-    async def test_switch_turn_off(self):
-        async with assert_device_properties_set(
-            self.switch._device, {SLEEP_DPS: False}
-        ):
-            await self.switch.async_turn_off()
-
-    async def test_toggle_turns_the_switch_on_when_it_was_off(self):
-        self.dps[SLEEP_DPS] = False
-
-        async with assert_device_properties_set(self.switch._device, {SLEEP_DPS: True}):
-            await self.switch.async_toggle()
-
-    async def test_toggle_turns_the_switch_off_when_it_was_on(self):
-        self.dps[SLEEP_DPS] = True
-
-        async with assert_device_properties_set(
-            self.switch._device, {SLEEP_DPS: False}
-        ):
-            await self.switch.async_toggle()
+        self.assertEqual(self.basicSwitch.icon, "mdi:power-sleep")

+ 5 - 33
tests/devices/test_saswell_c16_thermostat.py

@@ -14,7 +14,7 @@ from homeassistant.const import STATE_UNAVAILABLE
 
 from ..const import SASWELL_C16_THERMOSTAT_PAYLOAD
 from ..helpers import assert_device_properties_set
-from .base_device_tests import TuyaDeviceTestCase
+from .base_device_tests import BasicLockTests, TuyaDeviceTestCase
 
 TEMPERATURE_DPS = "2"
 PRESET_DPS = "3"
@@ -37,7 +37,7 @@ HVACACTION_DPS = "24"
 UNKNOWN26_DPS = "26"
 
 
-class TestSaswellC16Thermostat(TuyaDeviceTestCase):
+class TestSaswellC16Thermostat(BasicLockTests, TuyaDeviceTestCase):
     __test__ = True
 
     def setUp(self):
@@ -45,7 +45,7 @@ class TestSaswellC16Thermostat(TuyaDeviceTestCase):
             "saswell_c16_thermostat.yaml", SASWELL_C16_THERMOSTAT_PAYLOAD
         )
         self.subject = self.entities.get("climate")
-        self.lock = self.entities.get("lock_child_lock")
+        self.setUpBasicLock(LOCK_DPS, self.entities.get("lock_child_lock"))
 
     def test_supported_features(self):
         self.assertEqual(
@@ -61,9 +61,9 @@ class TestSaswellC16Thermostat(TuyaDeviceTestCase):
         self.dps[PRESET_DPS] = "Anti_frozen"
         self.assertEqual(self.subject.icon, "mdi:snowflake")
         self.dps[LOCK_DPS] = True
-        self.assertEqual(self.lock.icon, "mdi:hand-back-right-off")
+        self.assertEqual(self.basicLock.icon, "mdi:hand-back-right-off")
         self.dps[LOCK_DPS] = False
-        self.assertEqual(self.lock.icon, "mdi:hand-back-right")
+        self.assertEqual(self.basicLock.icon, "mdi:hand-back-right")
 
     def test_temperature_unit(self):
         self.assertEqual(
@@ -200,31 +200,3 @@ class TestSaswellC16Thermostat(TuyaDeviceTestCase):
                 "unknown_26": 26,
             },
         )
-
-    def test_lock_state(self):
-        self.dps[LOCK_DPS] = True
-        self.assertEqual(self.lock.state, STATE_LOCKED)
-
-        self.dps[LOCK_DPS] = False
-        self.assertEqual(self.lock.state, STATE_UNLOCKED)
-
-        self.dps[LOCK_DPS] = None
-        self.assertEqual(self.lock.state, STATE_UNAVAILABLE)
-
-    def test_lock_is_locked(self):
-        self.dps[LOCK_DPS] = True
-        self.assertTrue(self.lock.is_locked)
-
-        self.dps[LOCK_DPS] = False
-        self.assertFalse(self.lock.is_locked)
-
-        self.dps[LOCK_DPS] = None
-        self.assertFalse(self.lock.is_locked)
-
-    async def test_lock_locks(self):
-        async with assert_device_properties_set(self.lock._device, {LOCK_DPS: True}):
-            await self.lock.async_lock()
-
-    async def test_lock_unlocks(self):
-        async with assert_device_properties_set(self.lock._device, {LOCK_DPS: False}):
-            await self.lock.async_unlock()

+ 3 - 41
tests/devices/test_smartplugv1.py

@@ -3,7 +3,7 @@ from homeassistant.components.switch import DEVICE_CLASS_OUTLET
 
 from ..const import KOGAN_SOCKET_PAYLOAD
 from ..helpers import assert_device_properties_set
-from .base_device_tests import TuyaDeviceTestCase
+from .base_device_tests import SwitchableTests, TuyaDeviceTestCase
 
 SWITCH_DPS = "1"
 TIMER_DPS = "2"
@@ -12,55 +12,17 @@ POWER_DPS = "5"
 VOLTAGE_DPS = "6"
 
 
-class TestKoganSwitch(TuyaDeviceTestCase):
+class TestKoganSwitch(SwitchableTests, TuyaDeviceTestCase):
     __test__ = True
 
     def setUp(self):
         self.setUpForConfig("smartplugv1.yaml", KOGAN_SOCKET_PAYLOAD)
         self.subject = self.entities.get("switch")
+        self.setUpSwitchable(SWITCH_DPS, self.subject)
 
     def test_device_class_is_outlet(self):
         self.assertEqual(self.subject.device_class, DEVICE_CLASS_OUTLET)
 
-    def test_is_on(self):
-        self.dps[SWITCH_DPS] - True
-        self.assertTrue(self.subject.is_on)
-
-        self.dps[SWITCH_DPS] = False
-        self.assertFalse(self.subject.is_on)
-
-    def test_is_on_when_unavailable(self):
-        self.dps[SWITCH_DPS] = None
-        self.assertIsNone(self.subject.is_on)
-
-    async def test_turn_on(self):
-        async with assert_device_properties_set(
-            self.subject._device, {SWITCH_DPS: True}
-        ):
-            await self.subject.async_turn_on()
-
-    async def test_turn_off(self):
-        async with assert_device_properties_set(
-            self.subject._device, {SWITCH_DPS: False}
-        ):
-            await self.subject.async_turn_off()
-
-    async def test_toggle_turns_the_switch_on_when_it_was_off(self):
-        self.dps[SWITCH_DPS] = False
-
-        async with assert_device_properties_set(
-            self.subject._device, {SWITCH_DPS: True}
-        ):
-            await self.subject.async_toggle()
-
-    async def test_toggle_turns_the_switch_off_when_it_was_on(self):
-        self.dps[SWITCH_DPS] = True
-
-        async with assert_device_properties_set(
-            self.subject._device, {SWITCH_DPS: False}
-        ):
-            await self.subject.async_toggle()
-
     def test_current_power_w(self):
         self.dps[POWER_DPS] = 1234
         self.assertEqual(self.subject.current_power_w, 123.4)

+ 3 - 41
tests/devices/test_smartplugv2.py

@@ -3,7 +3,7 @@ from homeassistant.components.switch import DEVICE_CLASS_OUTLET
 
 from ..const import KOGAN_SOCKET_PAYLOAD2
 from ..helpers import assert_device_properties_set
-from .base_device_tests import TuyaDeviceTestCase
+from .base_device_tests import SwitchableTests, TuyaDeviceTestCase
 
 SWITCH_DPS = "1"
 TIMER_DPS = "9"
@@ -12,55 +12,17 @@ POWER_DPS = "19"
 VOLTAGE_DPS = "20"
 
 
-class TestSwitchV2(TuyaDeviceTestCase):
+class TestSwitchV2(SwitchableTests, TuyaDeviceTestCase):
     __test__ = True
 
     def setUp(self):
         self.setUpForConfig("smartplugv2.yaml", KOGAN_SOCKET_PAYLOAD2)
         self.subject = self.entities.get("switch")
+        self.setUpSwitchable(SWITCH_DPS, self.subject)
 
     def test_device_class_is_outlet(self):
         self.assertEqual(self.subject.device_class, DEVICE_CLASS_OUTLET)
 
-    def test_is_on(self):
-        self.dps[SWITCH_DPS] - True
-        self.assertTrue(self.subject.is_on)
-
-        self.dps[SWITCH_DPS] = False
-        self.assertFalse(self.subject.is_on)
-
-    def test_is_on_when_unavailable(self):
-        self.dps[SWITCH_DPS] = None
-        self.assertIsNone(self.subject.is_on)
-
-    async def test_turn_on(self):
-        async with assert_device_properties_set(
-            self.subject._device, {SWITCH_DPS: True}
-        ):
-            await self.subject.async_turn_on()
-
-    async def test_turn_off(self):
-        async with assert_device_properties_set(
-            self.subject._device, {SWITCH_DPS: False}
-        ):
-            await self.subject.async_turn_off()
-
-    async def test_toggle_turns_the_switch_on_when_it_was_off(self):
-        self.dps[SWITCH_DPS] = False
-
-        async with assert_device_properties_set(
-            self.subject._device, {SWITCH_DPS: True}
-        ):
-            await self.subject.async_toggle()
-
-    async def test_toggle_turns_the_switch_off_when_it_was_on(self):
-        self.dps[SWITCH_DPS] = True
-
-        async with assert_device_properties_set(
-            self.subject._device, {SWITCH_DPS: False}
-        ):
-            await self.subject.async_toggle()
-
     def test_current_power_w(self):
         self.dps[POWER_DPS] = 1234
         self.assertEqual(self.subject.current_power_w, 123.4)