Repository: yozik04/vallox_websocket_api (language: python)

Issue:
Remove asynctest
`asyntest` is [unmaintained](https://github.com/Martiusweb/asynctest/issues/163) and has issues with Python 3.11. The tests should be migrated.

The first distributions which are shipping `vallox-websocket-api` are seeing build failures. This is problematic as it's a Home Assistant dependency.


Discussion / hints:

Fix patch:
diff --git a/.gitignore b/.gitignore
index e90dabe..776d1a1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -93,4 +93,9 @@ ENV/
 .idea
 
 # other
-.DS_Store
\ No newline at end of file
+.DS_Store
+/html
+.mutmut-cache
+/Makefile
+/bin
+/service
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 4a6dbe7..421685b 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -1,6 +1,6 @@
 repos:
 - repo: https://github.com/pre-commit/pre-commit-hooks
-  rev: v4.1.0
+  rev: v4.4.0
   hooks:
   - id: check-added-large-files
   - id: check-case-conflict
@@ -16,13 +16,13 @@ repos:
   - id: trailing-whitespace
 
 - repo: https://github.com/asottile/pyupgrade
-  rev: v2.31.1
+  rev: v3.3.1
   hooks:
   - id: pyupgrade
     args: ["--py36-plus"]
 
 - repo: https://github.com/psf/black
-  rev: 22.3.0
+  rev: 23.1.0
   hooks:
   - id: black
     args:
@@ -30,23 +30,23 @@ repos:
       - --quiet
 
 - repo: https://github.com/pycqa/isort
-  rev: 5.10.1
+  rev: 5.12.0
   hooks:
   - id: isort
 
 - repo: https://github.com/PyCQA/flake8
-  rev: 4.0.1
+  rev: 6.0.0
   hooks:
   - id: flake8
     additional_dependencies: [flake8-bugbear]
 
 - repo: https://github.com/codespell-project/codespell
-  rev: v2.1.0
+  rev: v2.2.2
   hooks:
   - id: codespell
 
 - repo: https://github.com/pre-commit/pygrep-hooks
-  rev: v1.9.0
+  rev: v1.10.0
   hooks:
   - id: python-check-blanket-noqa
   - id: python-check-blanket-type-ignore
diff --git a/README.md b/README.md
index 342875c..31f2f93 100644
--- a/README.md
+++ b/README.md
@@ -14,7 +14,7 @@ Old sync version is available [1.5.x](https://pypi.org/project/vallox-websocket-
 Release notes for the new versions are [here](https://github.com/yozik04/vallox_websocket_api/releases)
 
 ## Requirements
-Python 3.6.0+
+Python 3.8.0+
 
 If you want to use Python 2.7, then use old sync version [1.5.x](https://pypi.org/project/vallox-websocket-api/1.5.2/)
 
diff --git a/pyproject.toml b/pyproject.toml
index c70b918..bbb2362 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -19,3 +19,6 @@ forced_separate = [
     "tests",
 ]
 combine_as_imports = true
+
+[tool.pytest.ini_options]
+asyncio_mode = "auto"
diff --git a/setup.cfg b/setup.cfg
index e57295a..3fd363f 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -17,11 +17,10 @@ classifiers =
     Operating System :: OS Independent
     Programming Language :: Python
     Programming Language :: Python :: 3 :: Only
-    Programming Language :: Python :: 3.6
-    Programming Language :: Python :: 3.7
     Programming Language :: Python :: 3.8
     Programming Language :: Python :: 3.9
     Programming Language :: Python :: 3.10
+    Programming Language :: Python :: 3.11
 license = LGPL 3
 
 [options]
@@ -33,11 +32,9 @@ python_requires = >=3.6.0, <4
 install_requires =
     websockets >= 9.1, < 11.0
     construct >= 2.9.0, < 3.0.0
-setup_requires =
-    wheel
 tests_require =
-    mock
-    asynctest
+    pytest
+    pytest-asyncio
 
 [flake8]
 exclude = .venv,.git,.tox,docs,venv,bin,lib,deps,build
diff --git a/tox.ini b/tox.ini
index 23a5758..108d3f9 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,5 +1,5 @@
 [tox]
-envlist = py36, py37, py38, py39, py310
+envlist = py38, py39, py310, py311
 isolated_build = True
 skip_missing_interpreters = True
 
@@ -7,5 +7,5 @@ skip_missing_interpreters = True
 changedir = tests
 deps =
     pytest
-    asynctest
+    pytest-asyncio
 commands = pytest --basetemp="{envtmpdir}"  {posargs}


Test patch:
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index f216ed7..51c29cb 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -16,7 +16,7 @@ jobs:
     strategy:
       fail-fast: false
       matrix:
-        python-version: ["3.6", "3.7", "3.8", "3.9", "3.10"]
+        python-version: ["3.8", "3.9", "3.10", "3.11"]
 
     steps:
     - uses: actions/checkout@v3
diff --git a/tests/conftest.py b/tests/conftest.py
new file mode 100644
index 0000000..5098786
--- /dev/null
+++ b/tests/conftest.py
@@ -0,0 +1,26 @@
+from unittest.mock import AsyncMock, patch
+
+import pytest
+import websockets
+
+from vallox_websocket_api.client import Client
+from vallox_websocket_api.vallox import Vallox
+
+
+@pytest.fixture
+def client():
+    return Client("127.0.0.1")
+
+
+@pytest.fixture
+def vallox():
+    return Vallox("127.0.0.1")
+
+
+@pytest.fixture
+def ws():
+    with patch("websockets.connect") as connect:
+        protocol_mock = AsyncMock(spec=websockets.WebSocketCommonProtocol)
+        connect.return_value.__aenter__.side_effect = protocol_mock
+
+        yield protocol_mock.return_value
diff --git a/tests/decorators.py b/tests/decorators.py
deleted file mode 100644
index e5a0178..0000000
--- a/tests/decorators.py
+++ /dev/null
@@ -1,17 +0,0 @@
-import asynctest
-import websockets
-
-from vallox_websocket_api.client import Client
-
-
-def with_client(func):
-    @asynctest.patch("websockets.connect")
-    async def wrapper(cls, connect):
-        client = Client("127.0.0.1")
-        instance = connect.return_value
-
-        protocol_mock = asynctest.create_autospec(websockets.WebSocketCommonProtocol)
-        instance.__aenter__.side_effect = protocol_mock
-        await func(cls, client, protocol_mock.return_value)
-
-    return wrapper
diff --git a/tests/test_client.py b/tests/test_client.py
index 075a8c2..878be42 100644
--- a/tests/test_client.py
+++ b/tests/test_client.py
@@ -1,7 +1,7 @@
 import binascii
+from unittest.mock import AsyncMock
 
-import asynctest
-from asynctest import CoroutineMock
+import pytest
 from websockets.exceptions import InvalidMessage
 
 from vallox_websocket_api.client import Client
@@ -10,125 +10,103 @@
     ValloxWebsocketException,
 )
 
-from tests.decorators import with_client
 
+async def test_fetch_metric(client: Client, ws):
+    ws.recv.return_value = binascii.unhexlify(
+        "0024000000000000000000000000000001000800030000000000000061df98b100030003203fb9500331000000000000000000560000000000000000000000000000000000000000001b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002b000f732a6ca969a171d1730800010000022700000028000000000000000001a6029e000100000028ffffffffffffffffffffffffffffffffffffffffffffffff000000000000000057c503e8ffffffffffff000000190000000000010000000000000000000300001b98012000a50000000000000000001e00010000000100000000000000000007001b000f001700010012000200070044000000010000000000000007003200320001000000000000001e0000c0a80501ffffff0000000000000000000000000000000000000000000000000000000000c0a8050c86076097f78844b7ac4db61e502fe4f2004c004c000100c00101001c001e000a00320000003703840000708f00320032000a0000000000010000000a721f0000000000010000000f728300000000000000000064715700000000000000000000000000000000000000010037001e000000000000000068bf71bb000083910000002600b4000000010001000000010001001e000f00080001001200000003000000000000000000000017000003e90000000000000001000100010000000a003200010000000000000000000000000000000000000000001000000000000000000000000000540048000000000000000000000000000000c8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a90000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
+    )
 
-class TestClient(asynctest.TestCase):
-    @with_client
-    async def testFetchMetric(self, client: Client, ws):
-        ws.recv.return_value = binascii.unhexlify(
-            "0024000000000000000000000000000001000800030000000000000061df98b100030003203fb9500331000000000000000000560000000000000000000000000000000000000000001b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002b000f732a6ca969a171d1730800010000022700000028000000000000000001a6029e000100000028ffffffffffffffffffffffffffffffffffffffffffffffff000000000000000057c503e8ffffffffffff000000190000000000010000000000000000000300001b98012000a50000000000000000001e00010000000100000000000000000007001b000f001700010012000200070044000000010000000000000007003200320001000000000000001e0000c0a80501ffffff0000000000000000000000000000000000000000000000000000000000c0a8050c86076097f78844b7ac4db61e502fe4f2004c004c000100c00101001c001e000a00320000003703840000708f00320032000a0000000000010000000a721f0000000000010000000f728300000000000000000064715700000000000000000000000000000000000000010037001e000000000000000068bf71bb000083910000002600b4000000010001000000010001001e000f00080001001200000003000000000000000000000017000003e90000000000000001000100010000000a003200010000000000000000000000000000000000000000001000000000000000000000000000540048000000000000000000000000000000c8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a9000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
-        )
+    result = await client.fetch_metric("A_CYC_ENABLED")
 
-        result = await client.fetch_metric("A_CYC_ENABLED")
+    assert result == 1
+    ws.send.assert_called_once_with(binascii.unhexlify("0300f6000000f900"))
 
-        self.assertEqual(1, result)
 
-        ws.send.assert_called_once_with(binascii.unhexlify("0300f6000000f900"))
+async def test_set_temp_value(client: Client, ws):
+    await client.set_values({"A_CYC_BOOST_AIR_TEMP_TARGET": "19"})
 
-    @with_client
-    async def testSetTempValue(self, client: Client, ws):
-        ws.recv.return_value = binascii.unhexlify("0200f500f700")
+    ws.send.assert_called_once_with(binascii.unhexlify("0400f90022501f723ec3"))
 
-        await client.set_values({"A_CYC_BOOST_AIR_TEMP_TARGET": "19"})
 
-        ws.send.assert_called_once_with(binascii.unhexlify("0400f90022501f723ec3"))
+async def test_set_temp_value_fraction(client: Client, ws):
+    await client.set_values({"A_CYC_BOOST_AIR_TEMP_TARGET": "19.1"})
 
-    @with_client
-    async def testSetTempValueFraction(self, client: Client, ws):
-        ws.recv.return_value = binascii.unhexlify("0200f500f700")
+    ws.send.assert_called_once_with(binascii.unhexlify("0400f9002250297248c3"))
 
-        await client.set_values({"A_CYC_BOOST_AIR_TEMP_TARGET": "19.1"})
 
-        ws.send.assert_called_once_with(binascii.unhexlify("0400f9002250297248c3"))
+async def test_set_temp_value_fraction_rounding1(client: Client, ws):
+    await client.set_values({"A_CYC_BOOST_AIR_TEMP_TARGET": "19.145"})
 
-    @with_client
-    async def testSetTempValueFractionRounding1(self, client: Client, ws):
-        ws.recv.return_value = binascii.unhexlify("0200f500f700")
+    ws.send.assert_called_once_with(binascii.unhexlify("0400f9002250297248c3"))
 
-        await client.set_values({"A_CYC_BOOST_AIR_TEMP_TARGET": "19.145"})
 
-        ws.send.assert_called_once_with(binascii.unhexlify("0400f9002250297248c3"))
+async def test_set_temp_value_fraction_rounding2(client: Client, ws):
+    await client.set_values({"A_CYC_BOOST_AIR_TEMP_TARGET": "18.991"})
 
-    @with_client
-    async def testSetTempValueFractionRounding2(self, client: Client, ws):
-        ws.recv.return_value = binascii.unhexlify("0200f500f700")
+    ws.send.assert_called_once_with(binascii.unhexlify("0400f90022501f723ec3"))
 
-        await client.set_values({"A_CYC_BOOST_AIR_TEMP_TARGET": "18.991"})
 
-        ws.send.assert_called_once_with(binascii.unhexlify("0400f90022501f723ec3"))
+async def test_set_value(client: Client, ws):
+    await client.set_values(
+        {
+            "A_CYC_STATE": 0,
+            "A_CYC_BOOST_TIMER": 0,
+            "A_CYC_FIREPLACE_TIMER": 0,
+            "A_CYC_EXTRA_TIMER": 0,
+        }
+    )
 
-    @with_client
-    async def testSetValue(self, client: Client, ws):
-        ws.recv.return_value = binascii.unhexlify("0200f500f700")
+    ws.send.assert_called_once_with(
+        binascii.unhexlify("0a00f900011200000412000005120000061200001349")
+    )
 
-        await client.set_values(
-            {
-                "A_CYC_STATE": 0,
-                "A_CYC_BOOST_TIMER": 0,
-                "A_CYC_FIREPLACE_TIMER": 0,
-                "A_CYC_EXTRA_TIMER": 0,
-            }
-        )
 
-        ws.send.assert_called_once_with(
-            binascii.unhexlify("0a00f900011200000412000005120000061200001349")
-        )
+async def test_set_assertion(client: Client, ws):
+    with pytest.raises(ValloxInvalidInputException):
+        await client.set_values({"A_CYC_BOOST_TIMER": "11.2"})
 
-    @with_client
-    async def testSetAssertion(self, client: Client, ws):
-        ws.recv.return_value = binascii.unhexlify("0200f500f700")
+    with pytest.raises(ValloxInvalidInputException):
+        await client.set_values({"A_CYC_BOOST_AIR_TEMP_TARGET": "11.a"})
 
-        with self.assertRaises(ValloxInvalidInputException):
-            await client.set_values({"A_CYC_BOOST_TIMER": "11.2"})
+    with pytest.raises(ValloxInvalidInputException):
+        await client.set_values({"A_CYC_BOOST_SPEED_SETTING": "11.2"})
 
-        with self.assertRaises(ValloxInvalidInputException):
-            await client.set_values({"A_CYC_BOOST_AIR_TEMP_TARGET": "11.a"})
+    with pytest.raises(ValloxInvalidInputException):
+        await client.set_values({"A_CYC_FIREPLACE_SUPP_FAN": "11.2"})
 
-        with self.assertRaises(ValloxInvalidInputException):
-            await client.set_values({"A_CYC_BOOST_SPEED_SETTING": "11.2"})
+    ws.send.assert_not_called()
 
-        with self.assertRaises(ValloxInvalidInputException):
-            await client.set_values({"A_CYC_FIREPLACE_SUPP_FAN": "11.2"})
 
-    @with_client
-    async def testSetMissing(self, client: Client, ws):
-        ws.recv.return_value = binascii.unhexlify("0200f500f700")
+async def test_set_missing(client: Client, ws):
+    with pytest.raises(ValloxInvalidInputException):
+        await client.set_values({"A_CYC_BOOSTER": 10})
 
-        with self.assertRaises(ValloxInvalidInputException):
-            await client.set_values({"A_CYC_BOOSTER": 10})
+    ws.send.assert_not_called()
 
-    @with_client
-    async def testSetUnsettable(self, client: Client, ws):
-        ws.recv.return_value = binascii.unhexlify("0200f500f700")
 
-        with self.assertRaises(ValloxInvalidInputException):
-            await client.set_values({"A_CYC_RH_VALUE": 22})
+async def test_set_unsettable(client: Client, ws):
+    with pytest.raises(ValloxInvalidInputException):
+        await client.set_values({"A_CYC_RH_VALUE": 22})
 
-    @with_client
-    async def testSetNewSettableAddressByName(self, client: Client, ws):
-        ws.recv.return_value = binascii.unhexlify("0200f500f700")
+    ws.send.assert_not_called()
 
-        client.set_settable_address("A_CYC_RH_VALUE", int)
 
-        await client.set_values({"A_CYC_RH_VALUE": 22})
+async def test_set_new_settable_address_by_name(client: Client, ws):
+    client.set_settable_address("A_CYC_RH_VALUE", int)
 
-    @with_client
-    async def testSetNewSettableAddressByAddress(self, client: Client, ws):
-        ws.recv.return_value = binascii.unhexlify("0200f500f700")
+    await client.set_values({"A_CYC_RH_VALUE": 22})
 
-        client.set_settable_address(4363, int)
 
-        await client.set_values({"A_CYC_RH_VALUE": 22})
+async def test_set_new_settable_address_by_address(client: Client, ws):
+    client.set_settable_address(4363, int)
 
-    @with_client
-    async def testSetNewSettableAddressByAddressException(self, client: Client, ws):
-        ws.recv.side_effect = CoroutineMock(side_effect=InvalidMessage())
+    await client.set_values({"A_CYC_RH_VALUE": 22})
 
-        client.set_settable_address(4363, int)
 
-        await self.assertAsyncRaisesRegex(
-            ValloxWebsocketException,
-            "Websocket handshake failed",
-            client.set_values({"A_CYC_RH_VALUE": 22}),
-        )
+async def test_set_new_settable_address_by_address_exception(client: Client, ws):
+    ws.recv.side_effect = AsyncMock(side_effect=InvalidMessage())
+
+    client.set_settable_address(4363, int)
+
+    with pytest.raises(ValloxWebsocketException):
+        await client.set_values({"A_CYC_RH_VALUE": 22})
diff --git a/tests/test_fetch_logs.py b/tests/test_fetch_logs.py
index 48034b0..7a69440 100644
--- a/tests/test_fetch_logs.py
+++ b/tests/test_fetch_logs.py
@@ -1,57 +1,35 @@
 import datetime
-import struct
 from os import path
-
-import asynctest
-import websockets
+import struct
 
 from vallox_websocket_api import Vallox
 
 
-def with_vallox(func):
-    @asynctest.patch("websockets.connect")
-    async def wrapper(cls, connect):
-        client = Vallox("127.0.0.1")
-        client.set_values = asynctest.CoroutineMock()
-        instance = connect.return_value
-
-        protocol_mock = asynctest.create_autospec(websockets.WebSocketCommonProtocol)
-        instance.__aenter__.side_effect = protocol_mock
-        await func(cls, client, protocol_mock.return_value)
-
-    return wrapper
-
-
-class TestValloxRawLogs(asynctest.TestCase):
-    @with_vallox
-    async def testFetchRawLogs(self, client, ws):
-        """
-        IoQueue.KItemTypeLog = 1
-        VlxDevConstants.WS_WEB_UI_COMMAND_LOG_RAW = 243
-        checksum = 245
-        Uint16Array(3) [2, 243, 245]
-        :return:
-        """
-
-        resp1 = b"\x03\x00\xf3\x00\x06\x00\xfc\x00"
-        with open(path.join(path.dirname(__file__), "log_data.bin"), "rb") as f:
-            resp2 = f.read()
-        ws.recv.side_effect = [resp1, resp2]
-
-        data = await client.fetch_raw_logs()
-
-        ws.send.assert_called_once_with(struct.pack("HHH", 2, 243, 245))
-
-        self.assertEqual(6, len(data))
-        self.assertEqual(1308, len(data[0]))
-        self.assertEqual(8192, len(data[1]))
-
-        self.assertEqual(
-            data[0][0],
-            {
-                "id": 0,
-                "name": "extract_air_temp",
-                "date": datetime.datetime(2019, 7, 21, 10, 18),
-                "value": 24.1,
-            },
-        )
+async def test_fetch_raw_logs(vallox: Vallox, ws):
+    """
+    IoQueue.KItemTypeLog = 1
+    VlxDevConstants.WS_WEB_UI_COMMAND_LOG_RAW = 243
+    checksum = 245
+    Uint16Array(3) [2, 243, 245]
+    :return:
+    """
+
+    resp1 = b"\x03\x00\xf3\x00\x06\x00\xfc\x00"
+    with open(path.join(path.dirname(__file__), "log_data.bin"), "rb") as f:
+        resp2 = f.read()
+    ws.recv.side_effect = [resp1, resp2]
+
+    data = await vallox.fetch_raw_logs()
+
+    ws.send.assert_called_once_with(struct.pack("HHH", 2, 243, 245))
+
+    assert len(data) == 6
+    assert len(data[0]) == 1308
+    assert len(data[1]) == 8192
+
+    assert data[0][0] == {
+        "id": 0,
+        "name": "extract_air_temp",
+        "date": datetime.datetime(2019, 7, 21, 10, 18),
+        "value": 24.1,
+    }
diff --git a/tests/test_messages.py b/tests/test_messages.py
index 33f0b78..4423df8 100644
--- a/tests/test_messages.py
+++ b/tests/test_messages.py
@@ -1,70 +1,69 @@
 import binascii
 import struct
-from unittest import TestCase
 
 from vallox_websocket_api.client import calculate_offset, to_celsius
 from vallox_websocket_api.constants import vlxDevConstants
-from vallox_websocket_api.messages import (LogReadRequest, LogReadResponse1,
-                                           ReadTableRequest, ReadTableResponse,
-                                           WriteMessageRequest)
+from vallox_websocket_api.messages import (
+    LogReadRequest,
+    LogReadResponse1,
+    ReadTableRequest,
+    ReadTableResponse,
+    WriteMessageRequest,
+)
 
 
-class TestMessages(TestCase):
-    def test_read_table_message_request(self):
-        expected = struct.pack("HHHH", 3, 246, 0, 249)
+def test_read_table_message_request():
+    expected = struct.pack("HHHH", 3, 246, 0, 249)
 
-        res = ReadTableRequest.build({})
+    res = ReadTableRequest.build({})
 
-        self.assertEqual(expected, res)
+    assert expected == res
 
-    def test_write_message_request(self):
-        expected = struct.pack("HHHHH", 4, 249, 20508, 29215, 49976)
 
-        res = WriteMessageRequest.build(
-            {
-                "fields": {
-                    "value": {
-                        "items": [
-                            {
-                                "address": vlxDevConstants.A_CYC_HOME_AIR_TEMP_TARGET,
-                                "value": 29215,
-                            }
-                        ]
-                    }
+def test_write_message_request():
+    expected = struct.pack("HHHHH", 4, 249, 20508, 29215, 49976)
+
+    res = WriteMessageRequest.build(
+        {
+            "fields": {
+                "value": {
+                    "items": [
+                        {
+                            "address": vlxDevConstants.A_CYC_HOME_AIR_TEMP_TARGET,
+                            "value": 29215,
+                        }
+                    ]
                 }
             }
-        )
+        }
+    )
 
-        self.assertEqual(expected, res)
+    assert expected == res
 
-    def test_log_read_message(self):
-        expected = struct.pack("HHH", 2, 243, 245)
 
-        res = LogReadRequest.build({})
+def test_log_read_message():
+    expected = struct.pack("HHH", 2, 243, 245)
 
-        self.assertEqual(expected, res)
+    res = LogReadRequest.build({})
 
-    def test_read_table_message_response(self):
-        response = binascii.unhexlify(
-            "0024000000000000000000000000000001000800030000000000000061df98b100030003203fb9500331000000000000000000560000000000000000000000000000000000000000001b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002b000f732a6ca969a171d1730800010000022700000028000000000000000001a6029e000100000028ffffffffffffffffffffffffffffffffffffffffffffffff000000000000000057c503e8ffffffffffff000000190000000000010000000000000000000300001b98012000a50000000000000000001e00010000000100000000000000000007001b000f001700010012000200070044000000010000000000000007003200320001000000000000001e0000c0a80501ffffff0000000000000000000000000000000000000000000000000000000000c0a8050c86076097f78844b7ac4db61e502fe4f2004c004c000100c00101001c001e000a00320000003703840000708f00320032000a0000000000010000000a721f0000000000010000000f728300000000000000000064715700000000000000000000000000000000000000010037001e000000000000000068bf71bb000083910000002600b4000000010001000000010001001e000f00080001001200000003000000000000000000000017000003e90000000000000001000100010000000a003200010000000000000000000000000000000000000000001000000000000000000000000000540048000000000000000000000000000000c8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a9000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
-        )
+    assert expected == res
 
-        data = ReadTableResponse.parse(response)
-
-        self.assertEqual(
-            20.0,
-            to_celsius(
-                data[
-                    calculate_offset(
-                        vlxDevConstants.__dict__["A_CYC_HOME_AIR_TEMP_TARGET"]
-                    )
-                ]
-            ),
+
+def test_read_table_message_response():
+    data = ReadTableResponse.parse(
+        binascii.unhexlify(
+            "0024000000000000000000000000000001000800030000000000000061df98b100030003203fb9500331000000000000000000560000000000000000000000000000000000000000001b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002b000f732a6ca969a171d1730800010000022700000028000000000000000001a6029e000100000028ffffffffffffffffffffffffffffffffffffffffffffffff000000000000000057c503e8ffffffffffff000000190000000000010000000000000000000300001b98012000a50000000000000000001e00010000000100000000000000000007001b000f001700010012000200070044000000010000000000000007003200320001000000000000001e0000c0a80501ffffff0000000000000000000000000000000000000000000000000000000000c0a8050c86076097f78844b7ac4db61e502fe4f2004c004c000100c00101001c001e000a00320000003703840000708f00320032000a0000000000010000000a721f0000000000010000000f728300000000000000000064715700000000000000000000000000000000000000010037001e000000000000000068bf71bb000083910000002600b4000000010001000000010001001e000f00080001001200000003000000000000000000000017000003e90000000000000001000100010000000a003200010000000000000000000000000000000000000000001000000000000000000000000000540048000000000000000000000000000000c80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a9000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
         )
+    )
+
+    assert 20.0 == to_celsius(
+        data[calculate_offset(vlxDevConstants.__dict__["A_CYC_HOME_AIR_TEMP_TARGET"])]
+    )
+
 
-    def test_log_read_response(self):
-        response = struct.pack("HHHH", 3, 243, 6, 252)
+def test_log_read_response():
+    response = struct.pack("HHHH", 3, 243, 6, 252)
 
-        result = LogReadResponse1.parse(response)
+    result = LogReadResponse1.parse(response)
 
-        self.assertEqual(6, result.fields.value.pages)
+    assert 6 == result.fields.value.pages
diff --git a/tests/test_set_temperature.py b/tests/test_set_temperature.py
index 735fcd4..71f5929 100644
--- a/tests/test_set_temperature.py
+++ b/tests/test_set_temperature.py
@@ -1,38 +1,26 @@
 import struct
 
-import asynctest
 
-from tests.decorators import with_client
+async def testSetHomeAirTempTargetRawInt(client, ws):
+    # [4, 249, 20508, 29215, 49976]
+    ws.recv.return_value = b""
 
+    await client.set_values({"A_CYC_HOME_AIR_TEMP_TARGET": 19})
 
-class TestClient(asynctest.TestCase):
-    # [4, 249, 20508, 29215, 49976]
-    @with_client
-    async def testSetHomeAirTempTargetRawInt(self, client, ws):
-        ws.recv.return_value = b""
+    ws.send.assert_called_once_with(struct.pack("HHHHH", 4, 249, 20508, 29215, 49976))
 
-        await client.set_values({"A_CYC_HOME_AIR_TEMP_TARGET": 19})
 
-        ws.send.assert_called_once_with(
-            struct.pack("HHHHH", 4, 249, 20508, 29215, 49976)
-        )
+async def testSetHomeAirTempTargetRawFloat(client, ws):
+    ws.recv.return_value = b""
 
-    @with_client
-    async def testSetHomeAirTempTargetRawFloat(self, client, ws):
-        ws.recv.return_value = b""
+    await client.set_values({"A_CYC_HOME_AIR_TEMP_TARGET": 19.0})
 
-        await client.set_values({"A_CYC_HOME_AIR_TEMP_TARGET": 19.0})
+    ws.send.assert_called_once_with(struct.pack("HHHHH", 4, 249, 20508, 29215, 49976))
 
-        ws.send.assert_called_once_with(
-            struct.pack("HHHHH", 4, 249, 20508, 29215, 49976)
-        )
 
-    @with_client
-    async def testSetHomeAirTempTargetRawString(self, client, ws):
-        ws.recv.return_value = b""
+async def testSetHomeAirTempTargetRawString(client, ws):
+    ws.recv.return_value = b""
 
-        await client.set_values({"A_CYC_HOME_AIR_TEMP_TARGET": "19"})
+    await client.set_values({"A_CYC_HOME_AIR_TEMP_TARGET": "19"})
 
-        ws.send.assert_called_once_with(
-            struct.pack("HHHHH", 4, 249, 20508, 29215, 49976)
-        )
+    ws.send.assert_called_once_with(struct.pack("HHHHH", 4, 249, 20508, 29215, 49976))
diff --git a/tests/test_vallox_fan_speed.py b/tests/test_vallox_fan_speed.py
index 5a1dccc..4239d53 100644
--- a/tests/test_vallox_fan_speed.py
+++ b/tests/test_vallox_fan_speed.py
@@ -1,65 +1,54 @@
-import asynctest
+from unittest import mock
+
+import pytest
 
-from vallox_websocket_api import PROFILE, Vallox
 from vallox_websocket_api.exceptions import ValloxInvalidInputException
+from vallox_websocket_api.vallox import PROFILE, Vallox
+
+
+@pytest.fixture
+def vallox():
+    client = Vallox("127.0.0.1")
+    client.set_values = mock.AsyncMock()
+    client.fetch_metrics = mock.AsyncMock()
+    return client
+
 
+async def test_set_fan_speed_home(vallox: Vallox):
+    await vallox.set_fan_speed(PROFILE.HOME, 19)
 
-class TestValloxSetFanSpeed(asynctest.TestCase):
-    def setUp(self):
-        self.client = Vallox("127.0.0.1")
+    vallox.set_values.assert_called_once_with({"A_CYC_HOME_SPEED_SETTING": 19})
 
-        self.client.set_values = asynctest.CoroutineMock()
 
-    async def checkSetFanSpeed(self, profile, percentage, set_values_dict):
-        await self.client.set_fan_speed(profile, percentage)
+async def test_set_fan_speed_away(vallox: Vallox):
+    await vallox.set_fan_speed(PROFILE.AWAY, 0)
 
-        self.client.set_values.assert_called_once_with(set_values_dict)
+    vallox.set_values.assert_called_once_with({"A_CYC_AWAY_SPEED_SETTING": 0})
 
-    async def testSetFanSpeedHome(self):
-        await self.checkSetFanSpeed(PROFILE.HOME, 19, {"A_CYC_HOME_SPEED_SETTING": 19})
 
-    async def testSetFanSpeedAway(self):
-        await self.checkSetFanSpeed(PROFILE.AWAY, 0, {"A_CYC_AWAY_SPEED_SETTING": 0})
+async def test_set_fan_speed_boost(vallox: Vallox):
+    await vallox.set_fan_speed(PROFILE.BOOST, 100)
 
-    async def testSetFanSpeedBoost(self):
-        await self.checkSetFanSpeed(
-            PROFILE.BOOST, 100, {"A_CYC_BOOST_SPEED_SETTING": 100}
-        )
+    vallox.set_values.assert_called_once_with({"A_CYC_BOOST_SPEED_SETTING": 100})
 
-    async def testSetFanSpeedWrong(self):
-        with self.assertRaises(ValloxInvalidInputException):
-            await self.checkSetFanSpeed(PROFILE.FIREPLACE, 19, {"WRONG": 20})
 
-    async def testSetFanSpeedHomeInvalidPercentage(self):
-        with self.assertRaises(ValloxInvalidInputException):
-            await self.checkSetFanSpeed(
-                PROFILE.HOME, -1, {"A_CYC_HOME_SPEED_SETTING": -1}
-            )
+async def test_set_fan_speed_wrong(vallox: Vallox):
+    with pytest.raises(ValloxInvalidInputException):
+        await vallox.set_fan_speed(PROFILE.FIREPLACE, 19)
 
-        with self.assertRaises(ValloxInvalidInputException):
-            await self.checkSetFanSpeed(
-                PROFILE.HOME, 101, {"A_CYC_HOME_SPEED_SETTING": 101}
-            )
 
+async def test_set_fan_speed_home_invalid_percentage(vallox: Vallox):
+    with pytest.raises(ValloxInvalidInputException):
+        await vallox.set_fan_speed(PROFILE.HOME, -1)
 
-class TestValloxGetFanSpeed(asynctest.TestCase):
-    def setUp(self):
-        self.client = Vallox("127.0.0.1")
 
-    async def checkGetFanSpeedForProfile(
-        self, fetch_metrics_result, profile, expected_percentage
-    ):
-        self.client.fetch_metrics = asynctest.CoroutineMock(
-            return_value=fetch_metrics_result
-        )
+async def test_set_fan_speed_home_invalid_percentage2(vallox: Vallox):
+    with pytest.raises(ValloxInvalidInputException):
+        await vallox.set_fan_speed(PROFILE.HOME, 101)
 
-        self.assertEqual(await self.client.get_fan_speed(profile), expected_percentage)
 
-        self.client.fetch_metrics.assert_called_once_with(
-            list(fetch_metrics_result.keys())
-        )
+async def test_get_fan_speed_for_profile_home(vallox: Vallox):
+    vallox.fetch_metrics.return_value = {"A_CYC_HOME_SPEED_SETTING": 19}
 
-    async def testGetFanSpeedForProfileHome(self):
-        await self.checkGetFanSpeedForProfile(
-            {"A_CYC_HOME_SPEED_SETTING": 19}, PROFILE.HOME, 19
-        )
+    assert await vallox.get_fan_speed(PROFILE.HOME) == 19
+    vallox.fetch_metrics.assert_called_once_with(["A_CYC_HOME_SPEED_SETTING"])
diff --git a/tests/test_vallox_info.py b/tests/test_vallox_info.py
index f61a36b..15f80d0 100644
--- a/tests/test_vallox_info.py
+++ b/tests/test_vallox_info.py
@@ -1,40 +1,44 @@
-import asynctest
-
-from vallox_websocket_api import Vallox
-
-
-class TestValloxInfo(asynctest.TestCase):
-    def setUp(self):
-        self.client = Vallox("127.0.0.1")
-
-    async def testGetInfo(self):
-        self.client.fetch_metrics = asynctest.CoroutineMock(
-            return_value={
-                'A_CYC_APPL_SW_VERSION_1': 0,
-                'A_CYC_APPL_SW_VERSION_2': 0,
-                'A_CYC_APPL_SW_VERSION_3': 0,
-                'A_CYC_APPL_SW_VERSION_4': 0,
-                'A_CYC_APPL_SW_VERSION_5': 0,
-                'A_CYC_APPL_SW_VERSION_6': 0,
-                'A_CYC_APPL_SW_VERSION_7': 512,
-                'A_CYC_APPL_SW_VERSION_8': 0,
-                'A_CYC_APPL_SW_VERSION_9': 512,
-                'A_CYC_MACHINE_MODEL': 3,
-                'A_CYC_UUID0': 25432,
-                'A_CYC_UUID1': 3772,
-                'A_CYC_UUID2': 25432,
-                'A_CYC_UUID3': 0,
-                'A_CYC_UUID4': 25432,
-                'A_CYC_UUID5': 25432,
-                'A_CYC_UUID6': 25432,
-                'A_CYC_UUID7': 25432,
-            }
-        )
-
-        info = await self.client.get_info()
-
-        self.assertEqual("Vallox 145 MV", info["model"])
-        self.assertEqual("2.0.2", info["sw_version"])
-        self.assertEqual("63580ebc-6358-0000-6358-635863586358", str(info["uuid"]))
-
-        self.client.fetch_metrics.assert_called()
+from unittest import mock
+
+import pytest
+
+from vallox_websocket_api.vallox import Vallox
+
+
+@pytest.fixture
+def vallox():
+    client = Vallox("127.0.0.1")
+    client.fetch_metrics = mock.AsyncMock(
+        return_value={
+            "A_CYC_APPL_SW_VERSION_1": 0,
+            "A_CYC_APPL_SW_VERSION_2": 0,
+            "A_CYC_APPL_SW_VERSION_3": 0,
+            "A_CYC_APPL_SW_VERSION_4": 0,
+            "A_CYC_APPL_SW_VERSION_5": 0,
+            "A_CYC_APPL_SW_VERSION_6": 0,
+            "A_CYC_APPL_SW_VERSION_7": 512,
+            "A_CYC_APPL_SW_VERSION_8": 0,
+            "A_CYC_APPL_SW_VERSION_9": 512,
+            "A_CYC_MACHINE_MODEL": 3,
+            "A_CYC_UUID0": 25432,
+            "A_CYC_UUID1": 3772,
+            "A_CYC_UUID2": 25432,
+            "A_CYC_UUID3": 0,
+            "A_CYC_UUID4": 25432,
+            "A_CYC_UUID5": 25432,
+            "A_CYC_UUID6": 25432,
+            "A_CYC_UUID7": 25432,
+        }
+    )
+
+    return client
+
+
+async def test_get_info(vallox: Vallox):
+    info = await vallox.get_info()
+
+    assert info["model"] == "Vallox 145 MV"
+    assert info["sw_version"] == "2.0.2"
+    assert str(info["uuid"]) == "63580ebc-6358-0000-6358-635863586358"
+
+    vallox.fetch_metrics.assert_called()
diff --git a/tests/test_vallox_next_filter_change.py b/tests/test_vallox_next_filter_change.py
index f762ae0..97175d2 100644
--- a/tests/test_vallox_next_filter_change.py
+++ b/tests/test_vallox_next_filter_change.py
@@ -1,76 +1,82 @@
 from datetime import date
-import asynctest
-
-from vallox_websocket_api import PROFILE, Vallox
-
-class TestValloxNextFilterChange(asynctest.TestCase):
-    def setUp(self):
-        self.client = Vallox("127.0.0.1")
-
-    async def checkNextFilterChangeDate(self, fetch_metrics_result, expected_date):
-        self.client.fetch_metrics = asynctest.CoroutineMock(
-            return_value=fetch_metrics_result
-        )
-
-        self.assertEqual(await self.client.get_next_filter_change_date(), expected_date)
-
-        self.client.fetch_metrics.assert_called_once_with(
-            [
-                "A_CYC_FILTER_CHANGED_YEAR",
-                "A_CYC_FILTER_CHANGED_MONTH",
-                "A_CYC_FILTER_CHANGED_DAY",
-                "A_CYC_FILTER_CHANGE_INTERVAL",
-            ]
-        )
-
-    async def testNextFilterChangeInTheFuture(self):
-        await self.checkNextFilterChangeDate(
-            {
-                "A_CYC_FILTER_CHANGED_YEAR": 22,
-                "A_CYC_FILTER_CHANGED_MONTH": 1,
-                "A_CYC_FILTER_CHANGED_DAY": 30,
-                "A_CYC_FILTER_CHANGE_INTERVAL": 180,
-            },
-            date(2022, 7, 29),
-        )
-
-    async def testNextFilterChangeTomorrow(self):
-        await self.checkNextFilterChangeDate(
-            {
-                "A_CYC_FILTER_CHANGED_YEAR": 21,
-                "A_CYC_FILTER_CHANGED_MONTH": 10,
-                "A_CYC_FILTER_CHANGED_DAY": 19,
-                "A_CYC_FILTER_CHANGE_INTERVAL": 120,
-            },
-            date(2022, 2, 16),
-        )
-
-    async def testNextFilterChangeToday(self):
-        await self.checkNextFilterChangeDate(
-            {
-                "A_CYC_FILTER_CHANGED_YEAR": 21,
-                "A_CYC_FILTER_CHANGED_MONTH": 10,
-                "A_CYC_FILTER_CHANGED_DAY": 18,
-                "A_CYC_FILTER_CHANGE_INTERVAL": 120,
-            },
-            date(2022, 2, 15),
-        )
-
-    async def testNextFilterChangeYesterday(self):
-        await self.checkNextFilterChangeDate(
-            {
-                "A_CYC_FILTER_CHANGED_YEAR": 21,
-                "A_CYC_FILTER_CHANGED_MONTH": 10,
-                "A_CYC_FILTER_CHANGED_DAY": 17,
-                "A_CYC_FILTER_CHANGE_INTERVAL": 120,
-            },
-            date(2022, 2, 14),
-        )
-
-    async def testNoNextFilterChangeAvailable(self):
-        await self.checkNextFilterChangeDate(
-            {
-                "A_CYC_FILTER_CHANGE_INTERVAL": 120,
-            },
-            None,
-        )
+from unittest import mock
+
+from vallox_websocket_api import Vallox
+
+
+async def test_checkNextFilterChangeDate(vallox: Vallox):
+    vallox.fetch_metrics = mock.AsyncMock(
+        return_value={
+            "A_CYC_FILTER_CHANGED_YEAR": 22,
+            "A_CYC_FILTER_CHANGED_MONTH": 1,
+            "A_CYC_FILTER_CHANGED_DAY": 30,
+            "A_CYC_FILTER_CHANGE_INTERVAL": 180,
+        }
+    )
+
+    assert await vallox.get_next_filter_change_date() == date(2022, 7, 29)
+    assert_filter_metrics_called(vallox)
+
+
+async def test_nextFilterChangeInTheFuture(vallox: Vallox):
+    vallox.fetch_metrics = mock.AsyncMock(
+        return_value={
+            "A_CYC_FILTER_CHANGED_YEAR": 21,
+            "A_CYC_FILTER_CHANGED_MONTH": 10,
+            "A_CYC_FILTER_CHANGED_DAY": 19,
+            "A_CYC_FILTER_CHANGE_INTERVAL": 120,
+        }
+    )
+
+    assert await vallox.get_next_filter_change_date() == date(2022, 2, 16)
+    assert_filter_metrics_called(vallox)
+
+
+async def test_nextFilterChangeTomorrow(vallox: Vallox):
+    vallox.fetch_metrics = mock.AsyncMock(
+        return_value={
+            "A_CYC_FILTER_CHANGED_YEAR": 21,
+            "A_CYC_FILTER_CHANGED_MONTH": 10,
+            "A_CYC_FILTER_CHANGED_DAY": 18,
+            "A_CYC_FILTER_CHANGE_INTERVAL": 120,
+        }
+    )
+
+    assert await vallox.get_next_filter_change_date() == date(2022, 2, 15)
+    assert_filter_metrics_called(vallox)
+
+
+async def test_nextFilterChangeToday(vallox: Vallox):
+    vallox.fetch_metrics = mock.AsyncMock(
+        return_value={
+            "A_CYC_FILTER_CHANGED_YEAR": 21,
+            "A_CYC_FILTER_CHANGED_MONTH": 10,
+            "A_CYC_FILTER_CHANGED_DAY": 17,
+            "A_CYC_FILTER_CHANGE_INTERVAL": 120,
+        }
+    )
+
+    assert await vallox.get_next_filter_change_date() == date(2022, 2, 14)
+    assert_filter_metrics_called(vallox)
+
+
+async def test_nextFilterChangeYesterday(vallox: Vallox):
+    vallox.fetch_metrics = mock.AsyncMock(
+        return_value={
+            "A_CYC_FILTER_CHANGE_INTERVAL": 120,
+        }
+    )
+
+    assert await vallox.get_next_filter_change_date() is None
+    assert_filter_metrics_called(vallox)
+
+
+def assert_filter_metrics_called(vallox):
+    vallox.fetch_metrics.assert_called_once_with(
+        [
+            "A_CYC_FILTER_CHANGED_YEAR",
+            "A_CYC_FILTER_CHANGED_MONTH",
+            "A_CYC_FILTER_CHANGED_DAY",
+            "A_CYC_FILTER_CHANGE_INTERVAL",
+        ]
+    )
diff --git a/tests/test_vallox_profile.py b/tests/test_vallox_profile.py
index 316e4b6..09b1898 100644
--- a/tests/test_vallox_profile.py
+++ b/tests/test_vallox_profile.py
@@ -1,185 +1,209 @@
-import asynctest
+from unittest import mock
 
 from vallox_websocket_api import PROFILE, Vallox
 
 
-class TestValloxSetProfile(asynctest.TestCase):
-    def setUp(self):
-        self.client = Vallox("127.0.0.1")
-
-        self.client.set_values = asynctest.CoroutineMock()
-
-    async def checkSetProfile(self, profile, set_values_dict):
-        await self.client.set_profile(profile)
-
-        self.client.set_values.assert_called_once_with(set_values_dict)
-
-    async def testSetProfileHome(self):
-        await self.checkSetProfile(
-            PROFILE.HOME,
-            {
-                "A_CYC_STATE": "0",
-                "A_CYC_BOOST_TIMER": "0",
-                "A_CYC_FIREPLACE_TIMER": "0",
-                "A_CYC_EXTRA_TIMER": "0",
-            },
-        )
-
-    async def testSetProfileAway(self):
-        await self.checkSetProfile(
-            PROFILE.AWAY,
-            {
-                "A_CYC_STATE": "1",
-                "A_CYC_BOOST_TIMER": "0",
-                "A_CYC_FIREPLACE_TIMER": "0",
-                "A_CYC_EXTRA_TIMER": "0",
-            },
-        )
-
-    async def testSetProfileBoost(self):
-        self.client.fetch_metric = asynctest.CoroutineMock(return_value=30)
-
-        await self.checkSetProfile(
-            PROFILE.BOOST,
-            {
-                "A_CYC_BOOST_TIMER": "30",
-                "A_CYC_FIREPLACE_TIMER": "0",
-                "A_CYC_EXTRA_TIMER": "0",
-            },
-        )
-
-        self.client.fetch_metric.assert_called_once_with("A_CYC_BOOST_TIME")
-
-    async def testSetProfileFireplace(self):
-        self.client.fetch_metric = asynctest.CoroutineMock(return_value=30)
-
-        await self.checkSetProfile(
-            PROFILE.FIREPLACE,
-            {
-                "A_CYC_BOOST_TIMER": "0",
-                "A_CYC_FIREPLACE_TIMER": "30",
-                "A_CYC_EXTRA_TIMER": "0",
-            },
-        )
-
-        self.client.fetch_metric.assert_called_once_with("A_CYC_FIREPLACE_TIME")
-
-    async def testSetProfileExtra(self):
-        self.client.fetch_metric = asynctest.CoroutineMock(return_value=30)
-
-        await self.checkSetProfile(
-            PROFILE.EXTRA,
-            {
-                "A_CYC_BOOST_TIMER": "0",
-                "A_CYC_FIREPLACE_TIMER": "0",
-                "A_CYC_EXTRA_TIMER": "30",
-            },
-        )
-
-        self.client.fetch_metric.assert_called_once_with("A_CYC_EXTRA_TIME")
-
-
-class TestValloxGetProfile(asynctest.TestCase):
-    def setUp(self):
-        self.client = Vallox("127.0.0.1")
-
-    async def checkGetProfile(self, fetch_metrics_result, expected_profile):
-        self.client.fetch_metrics = asynctest.CoroutineMock(
-            return_value=fetch_metrics_result
-        )
-
-        self.assertEqual(await self.client.get_profile(), expected_profile)
-
-        self.client.fetch_metrics.assert_called_once_with(
-            [
-                "A_CYC_STATE",
-                "A_CYC_BOOST_TIMER",
-                "A_CYC_FIREPLACE_TIMER",
-                "A_CYC_EXTRA_TIMER",
-            ]
-        )
-
-    async def testGetProfileHome(self):
-        await self.checkGetProfile(
-            {
-                "A_CYC_STATE": 0,
-                "A_CYC_BOOST_TIMER": 0,
-                "A_CYC_FIREPLACE_TIMER": 0,
-                "A_CYC_EXTRA_TIMER": 0,
-            },
-            PROFILE.HOME,
-        )
-
-    async def testGetProfileAway(self):
-        await self.checkGetProfile(
-            {
-                "A_CYC_STATE": 1,
-                "A_CYC_BOOST_TIMER": 0,
-                "A_CYC_FIREPLACE_TIMER": 0,
-                "A_CYC_EXTRA_TIMER": 0,
-            },
-            PROFILE.AWAY,
-        )
-
-    async def testGetProfileBoost(self):
-        await self.checkGetProfile(
-            {
-                "A_CYC_STATE": 0,
-                "A_CYC_BOOST_TIMER": 30,
-                "A_CYC_FIREPLACE_TIMER": 0,
-                "A_CYC_EXTRA_TIMER": 0,
-            },
-            PROFILE.BOOST,
-        )
-
-        await self.checkGetProfile(
-            {
-                "A_CYC_STATE": 1,
-                "A_CYC_BOOST_TIMER": 30,
-                "A_CYC_FIREPLACE_TIMER": 0,
-                "A_CYC_EXTRA_TIMER": 0,
-            },
-            PROFILE.BOOST,
-        )
-
-    async def testGetProfileFireplace(self):
-        await self.checkGetProfile(
-            {
-                "A_CYC_STATE": 0,
-                "A_CYC_BOOST_TIMER": 0,
-                "A_CYC_FIREPLACE_TIMER": 30,
-                "A_CYC_EXTRA_TIMER": 0,
-            },
-            PROFILE.FIREPLACE,
-        )
-
-        await self.checkGetProfile(
-            {
-                "A_CYC_STATE": 1,
-                "A_CYC_BOOST_TIMER": 0,
-                "A_CYC_FIREPLACE_TIMER": 30,
-                "A_CYC_EXTRA_TIMER": 0,
-            },
-            PROFILE.FIREPLACE,
-        )
-
-    async def testGetProfileExtra(self):
-        await self.checkGetProfile(
-            {
-                "A_CYC_STATE": 0,
-                "A_CYC_BOOST_TIMER": 0,
-                "A_CYC_FIREPLACE_TIMER": 0,
-                "A_CYC_EXTRA_TIMER": 30,
-            },
-            PROFILE.EXTRA,
-        )
-
-        await self.checkGetProfile(
-            {
-                "A_CYC_STATE": 1,
-                "A_CYC_BOOST_TIMER": 0,
-                "A_CYC_FIREPLACE_TIMER": 0,
-                "A_CYC_EXTRA_TIMER": 30,
-            },
-            PROFILE.EXTRA,
-        )
+async def test_set_profile_home(vallox: Vallox):
+    vallox.set_values = mock.AsyncMock()
+
+    await vallox.set_profile(PROFILE.HOME)
+
+    vallox.set_values.assert_called_once_with(
+        {
+            "A_CYC_STATE": "0",
+            "A_CYC_BOOST_TIMER": "0",
+            "A_CYC_FIREPLACE_TIMER": "0",
+            "A_CYC_EXTRA_TIMER": "0",
+        }
+    )
+
+
+async def test_set_profile_away(vallox: Vallox):
+    vallox.set_values = mock.AsyncMock()
+
+    await vallox.set_profile(PROFILE.AWAY)
+
+    vallox.set_values.assert_called_once_with(
+        {
+            "A_CYC_STATE": "1",
+            "A_CYC_BOOST_TIMER": "0",
+            "A_CYC_FIREPLACE_TIMER": "0",
+            "A_CYC_EXTRA_TIMER": "0",
+        }
+    )
+
+
+async def test_set_profile_boost(vallox: Vallox):
+    vallox.set_values = mock.AsyncMock()
+    vallox.fetch_metric = mock.AsyncMock(return_value=30)
+
+    await vallox.set_profile(PROFILE.BOOST)
+
+    vallox.set_values.assert_called_once_with(
+        {
+            "A_CYC_BOOST_TIMER": "30",
+            "A_CYC_FIREPLACE_TIMER": "0",
+            "A_CYC_EXTRA_TIMER": "0",
+        }
+    )
+
+    vallox.fetch_metric.assert_called_once_with("A_CYC_BOOST_TIME")
+
+
+async def test_set_profile_fireplace(vallox: Vallox):
+    vallox.set_values = mock.AsyncMock()
+    vallox.fetch_metric = mock.AsyncMock(return_value=30)
+
+    await vallox.set_profile(PROFILE.FIREPLACE)
+
+    vallox.set_values.assert_called_once_with(
+        {
+            "A_CYC_BOOST_TIMER": "0",
+            "A_CYC_FIREPLACE_TIMER": "30",
+            "A_CYC_EXTRA_TIMER": "0",
+        }
+    )
+
+    vallox.fetch_metric.assert_called_once_with("A_CYC_FIREPLACE_TIME")
+
+
+async def test_set_profile_extra(vallox: Vallox):
+    vallox.set_values = mock.AsyncMock()
+    vallox.fetch_metric = mock.AsyncMock(return_value=30)
+
+    await vallox.set_profile(PROFILE.EXTRA)
+
+    vallox.set_values.assert_called_once_with(
+        {
+            "A_CYC_BOOST_TIMER": "0",
+            "A_CYC_FIREPLACE_TIMER": "0",
+            "A_CYC_EXTRA_TIMER": "30",
+        }
+    )
+
+    vallox.fetch_metric.assert_called_once_with("A_CYC_EXTRA_TIME")
+
+
+async def test_get_profile_home(vallox: Vallox):
+    vallox.fetch_metrics = mock.AsyncMock(
+        return_value={
+            "A_CYC_STATE": 0,
+            "A_CYC_BOOST_TIMER": 0,
+            "A_CYC_FIREPLACE_TIMER": 0,
+            "A_CYC_EXTRA_TIMER": 0,
+        }
+    )
+
+    assert await vallox.get_profile() == PROFILE.HOME
+
+    _assert_profile_metrics_fetched(vallox)
+
+
+async def test_get_profile_away(vallox: Vallox):
+    vallox.fetch_metrics = mock.AsyncMock(
+        return_value={
+            "A_CYC_STATE": 1,
+            "A_CYC_BOOST_TIMER": 0,
+            "A_CYC_FIREPLACE_TIMER": 0,
+            "A_CYC_EXTRA_TIMER": 0,
+        }
+    )
+
+    assert await vallox.get_profile() == PROFILE.AWAY
+
+    _assert_profile_metrics_fetched(vallox)
+
+
+async def test_get_profile_boost(vallox: Vallox):
+    vallox.fetch_metrics = mock.AsyncMock(
+        return_value={
+            "A_CYC_STATE": 0,
+            "A_CYC_BOOST_TIMER": 30,
+            "A_CYC_FIREPLACE_TIMER": 0,
+            "A_CYC_EXTRA_TIMER": 0,
+        }
+    )
+
+    assert await vallox.get_profile() == PROFILE.BOOST
+
+    _assert_profile_metrics_fetched(vallox)
+
+    vallox.fetch_metrics = mock.AsyncMock(
+        return_value={
+            "A_CYC_STATE": 1,
+            "A_CYC_BOOST_TIMER": 30,
+            "A_CYC_FIREPLACE_TIMER": 0,
+            "A_CYC_EXTRA_TIMER": 0,
+        }
+    )
+
+    assert await vallox.get_profile() == PROFILE.BOOST
+
+    _assert_profile_metrics_fetched(vallox)
+
+
+async def test_get_profile_fireplace(vallox: Vallox):
+    vallox.fetch_metrics = mock.AsyncMock(
+        return_value={
+            "A_CYC_STATE": 0,
+            "A_CYC_BOOST_TIMER": 0,
+            "A_CYC_FIREPLACE_TIMER": 30,
+            "A_CYC_EXTRA_TIMER": 0,
+        }
+    )
+
+    assert await vallox.get_profile() == PROFILE.FIREPLACE
+
+    _assert_profile_metrics_fetched(vallox)
+
+    vallox.fetch_metrics = mock.AsyncMock(
+        return_value={
+            "A_CYC_STATE": 1,
+            "A_CYC_BOOST_TIMER": 0,
+            "A_CYC_FIREPLACE_TIMER": 30,
+            "A_CYC_EXTRA_TIMER": 0,
+        }
+    )
+
+    assert await vallox.get_profile() == PROFILE.FIREPLACE
+
+    _assert_profile_metrics_fetched(vallox)
+
+
+async def test_get_profile_extra(vallox: Vallox):
+    vallox.fetch_metrics = mock.AsyncMock(
+        return_value={
+            "A_CYC_STATE": 0,
+            "A_CYC_BOOST_TIMER": 0,
+            "A_CYC_FIREPLACE_TIMER": 0,
+            "A_CYC_EXTRA_TIMER": 30,
+        }
+    )
+
+    assert await vallox.get_profile() == PROFILE.EXTRA
+
+    _assert_profile_metrics_fetched(vallox)
+
+    vallox.fetch_metrics = mock.AsyncMock(
+        return_value={
+            "A_CYC_STATE": 1,
+            "A_CYC_BOOST_TIMER": 0,
+            "A_CYC_FIREPLACE_TIMER": 0,
+            "A_CYC_EXTRA_TIMER": 30,
+        }
+    )
+
+    assert await vallox.get_profile() == PROFILE.EXTRA
+
+    _assert_profile_metrics_fetched(vallox)
+
+
+def _assert_profile_metrics_fetched(vallox):
+    vallox.fetch_metrics.assert_called_once_with(
+        [
+            "A_CYC_STATE",
+            "A_CYC_BOOST_TIMER",
+            "A_CYC_FIREPLACE_TIMER",
+            "A_CYC_EXTRA_TIMER",
+        ]
+    )
diff --git a/tests/test_vallox_temperatures.py b/tests/test_vallox_temperatures.py
index eb67ba4..5359bc9 100644
--- a/tests/test_vallox_temperatures.py
+++ b/tests/test_vallox_temperatures.py
@@ -1,84 +1,54 @@
 import binascii
 import struct
+from unittest import mock
 
-import asynctest
+import pytest
 
-from vallox_websocket_api import PROFILE, Vallox
+from vallox_websocket_api import PROFILE
 from vallox_websocket_api.exceptions import ValloxInvalidInputException
 
-from tests.decorators import with_client
 
-
-class TestValloxSetTemperature(asynctest.TestCase):
-    def setUp(self):
-        self.client = Vallox("127.0.0.1")
-
-        self.client.set_values = asynctest.CoroutineMock()
-
-    async def checkSetTemperature(self, profile, temperature, set_values_dict):
-        await self.client.set_temperature(profile, temperature)
-
-        self.client.set_values.assert_called_once_with(set_values_dict)
-
-    async def testSetTemperatureHome(self):
-        await self.checkSetTemperature(
-            PROFILE.HOME, 19, {"A_CYC_HOME_AIR_TEMP_TARGET": 19}
-        )
-
-    async def testSetTemperatureAway(self):
-        await self.checkSetTemperature(
-            PROFILE.AWAY, 19, {"A_CYC_AWAY_AIR_TEMP_TARGET": 19}
-        )
-
-    async def testSetTemperatureBoost(self):
-        await self.checkSetTemperature(
-            PROFILE.BOOST, 19, {"A_CYC_BOOST_AIR_TEMP_TARGET": 19}
-        )
-
-    async def testSetTemperatureWrong(self):
-        with self.assertRaises(ValloxInvalidInputException):
-            await self.checkSetTemperature(
-                PROFILE.FIREPLACE, 19, {"A_CYC_FIREPLACE_AIR_TEMP_TARGET": 19}
-            )
-
-
-class TestValloxGetTemperature(asynctest.TestCase):
-    def setUp(self):
-        self.client = Vallox("127.0.0.1")
-
-    async def checkGetTemperatureForProfile(
-        self, fetch_metrics_result, profile, expected_temperature
-    ):
-        self.client.fetch_metrics = asynctest.CoroutineMock(
-            return_value=fetch_metrics_result
-        )
-
-        self.assertEqual(
-            await self.client.get_temperature(profile), expected_temperature
-        )
-
-        self.client.fetch_metrics.assert_called_once_with(
-            list(fetch_metrics_result.keys())
-        )
-
-    async def testGetTemperatureForProfileHome(self):
-        await self.checkGetTemperatureForProfile(
-            {"A_CYC_HOME_AIR_TEMP_TARGET": 19}, PROFILE.HOME, 19
-        )
-
-    @with_client
-    async def testFetchMetric(self, client, ws):
-        """
-        IoQueue.KItemTypeFetch = 3
-        VlxDevConstants.WS_WEB_UI_COMMAND_READ_TABLES; = 246
-        item.value = 0;
-        checksum = 249
-        Uint16Array(4) [3, 246, 0, 249]
-        """
-        ws.recv.return_value = binascii.unhexlify(
-            "0024000000000000000000000000000001000800030000000000000061df98b100030003203fb9500331000000000000000000560000000000000000000000000000000000000000001b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002b000f732a6ca969a171d1730800010000022700000028000000000000000001a6029e000100000028ffffffffffffffffffffffffffffffffffffffffffffffff000000000000000057c503e8ffffffffffff000000190000000000010000000000000000000300001b98012000a50000000000000000001e00010000000100000000000000000007001b000f001700010012000200070044000000010000000000000007003200320001000000000000001e0000c0a80501ffffff0000000000000000000000000000000000000000000000000000000000c0a8050c86076097f78844b7ac4db61e502fe4f2004c004c000100c00101001c001e000a00320000003703840000708f00320032000a0000000000010000000a721f0000000000010000000f728300000000000000000064715700000000000000000000000000000000000000010037001e000000000000000068bf71bb000083910000002600b4000000010001000000010001001e000f00080001001200000003000000000000000000000017000003e90000000000000001000100010000000a003200010000000000000000000000000000000000000000001000000000000000000000000000540048000000000000000000000000000000c8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a9000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
-        )
-
-        self.assertEqual(20.0, await client.fetch_metric("A_CYC_HOME_AIR_TEMP_TARGET"))
-
-        ws.send.assert_called_once_with(struct.pack("HHHH", 3, 246, 0, 249))
+@pytest.mark.parametrize(
+    "profile, metric, value",
+    [
+        (PROFILE.HOME, "A_CYC_HOME_AIR_TEMP_TARGET", 19),
+        (PROFILE.AWAY, "A_CYC_AWAY_AIR_TEMP_TARGET", 19),
+        (PROFILE.BOOST, "A_CYC_BOOST_AIR_TEMP_TARGET", 19),
+    ],
+)
+async def test_vallox_set_temperature(vallox, profile, metric, value):
+    vallox.set_values = mock.AsyncMock()
+    await vallox.set_temperature(profile, value)
+    vallox.set_values.assert_called_once_with({metric: value})
+
+
+async def test_vallox_set_temperature_not_supported(vallox):
+    with pytest.raises(ValloxInvalidInputException):
+        await vallox.set_temperature(PROFILE.FIREPLACE, 19)
+
+
+async def test_vallox_get_temperature(vallox):
+    vallox.fetch_metrics = mock.AsyncMock(
+        return_value={"A_CYC_HOME_AIR_TEMP_TARGET": 19}
+    )
+
+    assert await vallox.get_temperature(PROFILE.HOME) == 19
+    vallox.fetch_metrics.assert_called_once_with(
+        list({"A_CYC_HOME_AIR_TEMP_TARGET": 19}.keys())
+    )
+
+
+async def test_vallox_fetch_metric(vallox, ws):
+    """
+    IoQueue.KItemTypeFetch = 3
+    VlxDevConstants.WS_WEB_UI_COMMAND_READ_TABLES; = 246
+    item.value = 0;
+    checksum = 249
+    Uint16Array(4) [3, 246, 0, 249]
+    """
+    ws.recv.return_value = binascii.unhexlify(
+        "0024000000000000000000000000000001000800030000000000000061df98b100030003203fb9500331000000000000000000560000000000000000000000000000000000000000001b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002b000f732a6ca969a171d1730800010000022700000028000000000000000001a6029e000100000028ffffffffffffffffffffffffffffffffffffffffffffffff000000000000000057c503e8ffffffffffff000000190000000000010000000000000000000300001b98012000a50000000000000000001e00010000000100000000000000000007001b000f001700010012000200070044000000010000000000000007003200320001000000000000001e0000c0a80501ffffff0000000000000000000000000000000000000000000000000000000000c0a8050c86076097f78844b7ac4db61e502fe4f2004c004c000100c00101001c001e000a00320000003703840000708f00320032000a0000000000010000000a721f0000000000010000000f728300000000000000000064715700000000000000000000000000000000000000010037001e000000000000000068bf71bb000083910000002600b4000000010001000000010001001e000f00080001001200000003000000000000000000000017000003e90000000000000001000100010000000a003200010000000000000000000000000000000000000000001000000000000000000000000000540048000000000000000000000000000000c8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a90000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
+    )
+
+    assert await vallox.fetch_metric("A_CYC_HOME_AIR_TEMP_TARGET") == 20.0
+    ws.send.assert_called_once_with(struct.pack("HHHH", 3, 246, 0, 249))