diff --git a/devices/clients/opcua_client.py b/devices/clients/opcua_client.py
index 872aee68cd62c2dc0f2edb1ce9975a713460eeb2..2f9e2ba17686ee9d71657f4ba174fd6e7ba9116c 100644
--- a/devices/clients/opcua_client.py
+++ b/devices/clients/opcua_client.py
@@ -121,7 +121,7 @@ class OPCUAConnection(AsyncCommClient):
         ua_type = numpy_to_OPCua_dict[attribute.numpy_type]	 # convert the numpy type to a corresponding UA type
 
         # configure and return the read/write functions
-        prot_attr = ProtocolAttribute(node, dim_x, dim_y, ua_type, self.event_loop)
+        prot_attr = ProtocolAttribute(node, dim_x, dim_y, ua_type)
 
         try:
             # NOTE: debug statement tries to get the qualified name, this may not always work. in that case forgo the name and just print the path
@@ -129,8 +129,16 @@ class OPCUAConnection(AsyncCommClient):
             self.streams.debug_stream("connected OPC ua node {} of type {} to attribute with dimensions: {} x {} ".format(str(node_name)[:len(node_name)-1], str(ua_type)[len("VariantType."):], dim_x, dim_y))
         except:
             pass
+
+        # Tango will call these from a separate polling thread.
+        def read_function():
+            asyncio.run_coroutine_threadsafe(prot_attr.read_function(), self.event_loop)
+
+        def write_function(value):
+            asyncio.run_coroutine_threadsafe(prot_attr.write_function(value), self.event_loop)
+
         # return the read/write functions
-        return prot_attr.read_function, prot_attr.write_function
+        return read_function, write_function
 
 
     async def call_method(self, method_path, *args):
@@ -143,23 +151,18 @@ class ProtocolAttribute:
     This class provides a small wrapper for the OPC ua read/write functions in order to better organise the code
     """
 
-    def __init__(self, node, dim_x, dim_y, ua_type, event_loop):
+    def __init__(self, node, dim_x, dim_y, ua_type):
         self.node = node
         self.dim_y = dim_y
         self.dim_x = dim_x
         self.ua_type = ua_type
 
-        self.event_loop = event_loop
-
-    async def _read_value(self):
-        return await self.node.get_value()
-
-    def read_function(self):
+    async def read_function(self):
         """
         Read_R function
         """
-        future = asyncio.run_coroutine_threadsafe(self._read_value(), self.event_loop)
-        value = future.result()
+
+        value = await self.node.get_value()
 
         try:
             if self.dim_y + self.dim_x == 1:
@@ -178,7 +181,18 @@ class ProtocolAttribute:
             raise ValueError(f"Failed to parse atribute value retrieved from OPC-UA: {value}") from e
 
 
-    async def _write_value(self, value):
+    async def write_function(self, value):
+        """
+        write_RW function
+        """
+
+        if self.dim_y != 0:
+            # flatten array, convert to python array
+            value = numpy.concatenate(value).tolist()
+        elif self.dim_x != 1:
+            # make sure it is a python array
+            value = value.tolist() if type(value) == numpy.ndarray else value
+
         try:
             await self.node.set_data_value(asyncua.ua.uatypes.Variant(Value=value, VariantType=self.ua_type))
         except (TypeError, asyncua.ua.uaerrors.BadTypeMismatch) as e:
@@ -212,19 +226,3 @@ class ProtocolAttribute:
             attribute_name = (await self.node.read_display_name()).to_string()
 
             raise TypeError(f"Cannot write value to OPC-UA attribute '{attribute_name}': tried to convert data type {our_type} to expected server type {expected_server_type}, server reports type {actual_server_type}") from e
-
-
-    def write_function(self, value):
-        """
-        write_RW function
-        """
-
-        if self.dim_y != 0:
-            # flatten array, convert to python array
-            value = numpy.concatenate(value).tolist()
-        elif self.dim_x != 1:
-            # make sure it is a python array
-            value = value.tolist() if type(value) == numpy.ndarray else value
-
-        future = asyncio.run_coroutine_threadsafe(self._write_value(value), self.event_loop)
-        _ = future.result()
diff --git a/devices/test/base.py b/devices/test/base.py
index 2bcbf59b33b605ba15faa0ad71c0fd53d80274ff..81a76c46e843dd7af91f19e1c142f612916157c7 100644
--- a/devices/test/base.py
+++ b/devices/test/base.py
@@ -9,6 +9,7 @@
 
 import unittest
 import testscenarios
+import asynctest
 
 
 class BaseTestCase(testscenarios.WithScenarios, unittest.TestCase):
@@ -23,3 +24,10 @@ class TestCase(BaseTestCase):
 
     def setUp(self):
         super().setUp()
+
+
+class AsyncTestCase(BaseTestCase):
+    """Test case base class for all asyncio unit tests."""
+
+    def setUp(self):
+        super().setUp()
diff --git a/devices/test/clients/test_opcua_client.py b/devices/test/clients/test_opcua_client.py
index 62ddd4a20d804df99cfbfb9a69c166ec322c931b..6315abb20547cb35233b9b47f8f8c32414b1159e 100644
--- a/devices/test/clients/test_opcua_client.py
+++ b/devices/test/clients/test_opcua_client.py
@@ -1,6 +1,6 @@
 import numpy
 from clients.opcua_client import OPCUAConnection
-from clients import opcua_client, comms_client
+from clients import opcua_client
 
 import asyncua
 import io
@@ -39,20 +39,10 @@ image_shape = (2, 3)
 dimension_tests = [scalar_shape, spectrum_shape, image_shape]
 
 
-class TestOPCua(base.TestCase):
-    def setUp(self):
-        self.comms_client = comms_client.AsyncCommClient()
-        self.event_loop = self.comms_client.event_loop
-
-    def tearDown(self):
-        # must explicitl delete, or python will mess up the descruction
-        # of the event loop
-        del self.event_loop
-        del self.comms_client
-
+class TestOPCua(base.AsyncTestCase):
     @asynctest.patch.object(OPCUAConnection, "ping")
     @asynctest.patch.object(opcua_client, "Client")
-    def test_opcua_connection(self, m_opc_client, m_ping):
+    async def test_opcua_connection(self, m_opc_client, m_ping):
         """
         This tests verifies whether the correct connection steps happen. It checks whether we can init an OPCUAConnection object
         Whether we can set the namespace, and the OPCua client.
@@ -65,26 +55,23 @@ class TestOPCua(base.TestCase):
         m_opc_client_members.send_hello = asynctest.asynctest.CoroutineMock()
         m_opc_client.return_value = m_opc_client_members
 
-        async def run_test():
-            test_client = OPCUAConnection("opc.tcp://localhost:4874/freeopcua/server/", "http://lofar.eu", 5, mock.Mock(), self.event_loop)
-            try:
-                await test_client.start()
-
-                m_opc_client.assert_called_once()  # makes sure the actual freeOPCua client object is created only once
+        test_client = OPCUAConnection("opc.tcp://localhost:4874/freeopcua/server/", "http://lofar.eu", 5, mock.Mock(), self.loop)
+        try:
+            await test_client.start()
 
-                # this also implies test_client.connect() is called
-                m_opc_client_members.get_namespace_index.assert_called_once_with("http://lofar.eu")
-                self.assertEqual(42, test_client.name_space_index)
-            finally:
-                await test_client.stop()
+            m_opc_client.assert_called_once()  # makes sure the actual freeOPCua client object is created only once
 
-        asyncio.run_coroutine_threadsafe(run_test(), self.event_loop).result()
+            # this also implies test_client.connect() is called
+            m_opc_client_members.get_namespace_index.assert_called_once_with("http://lofar.eu")
+            self.assertEqual(42, test_client.name_space_index)
+        finally:
+            await test_client.stop()
 
 
     @asynctest.patch.object(OPCUAConnection, "ping")
     @asynctest.patch.object(opcua_client, "Client")
     @mock.patch.object(opcua_client, 'ProtocolAttribute')
-    def test_opcua_attr_setup(self, m_protocol_attr, m_opc_client, m_ping):
+    async def test_opcua_attr_setup(self, m_protocol_attr, m_opc_client, m_ping):
         """
         This tests covers the correct creation of read/write functions.
         In normal circumstances called by he attribute wrapper.
@@ -103,42 +90,35 @@ class TestOPCua(base.TestCase):
         m_opc_client_members.get_objects_node = asynctest.Mock(return_value=m_objects_node)
         m_opc_client.return_value = m_opc_client_members
 
-        async def run_test():
-            for i in attr_test_types:
-                class mock_attr:
-                    def __init__(self, dtype, x, y):
-                        self.numpy_type = dtype
-                        self.dim_x = x
-                        self.dim_y = y
-
-                for j in dimension_tests:
-                    if len(j) == 1:
-                        dim_x = j[0]
-                        dim_y = 0
-                    else:
-                        dim_x = j[1]
-                        dim_y = j[0]
-
-                    # create a fake attribute with only the required variables in it.
-                    m_attribute = mock_attr(i.numpy_type, dim_x, dim_y)
-
-                    # pretend like there is a running OPCua server with a node that has this name
-                    m_annotation = ["2:PCC", f"2:testNode_{str(i.numpy_type)}_{str(dim_x)}_{str(dim_y)}"]
-
-                    test_client = OPCUAConnection("opc.tcp://localhost:4874/freeopcua/server/", "http://lofar.eu", 5, mock.Mock(), self.event_loop)
-                    try:
-                        await test_client.start()
-                        await test_client.setup_attribute(m_annotation, m_attribute)
-                    finally:
-                        await test_client.stop()
+        for i in attr_test_types:
+            class mock_attr:
+                def __init__(self, dtype, x, y):
+                    self.numpy_type = dtype
+                    self.dim_x = x
+                    self.dim_y = y
 
-                    # success if there are no errors.
+            for j in dimension_tests:
+                if len(j) == 1:
+                    dim_x = j[0]
+                    dim_y = 0
+                else:
+                    dim_x = j[1]
+                    dim_y = j[0]
 
-            await test_client.stop()
+                # create a fake attribute with only the required variables in it.
+                m_attribute = mock_attr(i.numpy_type, dim_x, dim_y)
 
-        asyncio.run_coroutine_threadsafe(run_test(), self.event_loop).result()
+                # pretend like there is a running OPCua server with a node that has this name
+                m_annotation = ["2:PCC", f"2:testNode_{str(i.numpy_type)}_{str(dim_x)}_{str(dim_y)}"]
 
+                test_client = OPCUAConnection("opc.tcp://localhost:4874/freeopcua/server/", "http://lofar.eu", 5, mock.Mock(), self.loop)
+                try:
+                    await test_client.start()
+                    await test_client.setup_attribute(m_annotation, m_attribute)
+                finally:
+                    await test_client.stop()
 
+                # success if there are no errors.
 
     def test_protocol_attr(self):
         """
@@ -160,7 +140,7 @@ class TestOPCua(base.TestCase):
                     dims = (j[1], j[0])
 
                 ua_type = opcua_client.numpy_to_OPCua_dict[i.numpy_type]
-                test = opcua_client.ProtocolAttribute(node, dims[0], dims[1], ua_type, self.event_loop)
+                test = opcua_client.ProtocolAttribute(node, dims[0], dims[1], ua_type)
                 print(test.dim_y, test.dim_x, test.ua_type)
 
                 """
@@ -173,7 +153,7 @@ class TestOPCua(base.TestCase):
                 self.assertTrue(hasattr(test, "write_function"), f"No write function found")
                 self.assertTrue(hasattr(test, "read_function"), f"No read function found")
 
-    def test_read(self):
+    async def test_read(self):
         """
         This tests the read functions.
         """
@@ -189,11 +169,11 @@ class TestOPCua(base.TestCase):
                 m_node = asynctest.asynctest.CoroutineMock()
 
                 if len(j) == 1:
-                    test = opcua_client.ProtocolAttribute(m_node, j[0], 0, opcua_client.numpy_to_OPCua_dict[i.numpy_type], self.event_loop)
+                    test = opcua_client.ProtocolAttribute(m_node, j[0], 0, opcua_client.numpy_to_OPCua_dict[i.numpy_type])
                 else:
-                    test = opcua_client.ProtocolAttribute(m_node, j[1], j[0], opcua_client.numpy_to_OPCua_dict[i.numpy_type], self.event_loop)
+                    test = opcua_client.ProtocolAttribute(m_node, j[1], j[0], opcua_client.numpy_to_OPCua_dict[i.numpy_type])
                 m_node.get_value = get_flat_value
-                val = test.read_function()
+                val = await test.read_function()
 
                 comp = val == get_test_value()
                 self.assertTrue(comp.all(), "Read value unequal to expected value: \n\t{} \n\t{}".format(val, get_test_value()))
@@ -233,7 +213,7 @@ class TestOPCua(base.TestCase):
 
 
 
-    def test_write(self):
+    async def test_write(self):
         """
         Test the writing of values by instantiating a ProtocolAttribute attribute, and calling the write function.
         but the opcua function that writes to the server has been changed to the compare_values function.
@@ -258,9 +238,9 @@ class TestOPCua(base.TestCase):
 
                 # create the protocolattribute
                 if len(j) == 1:
-                    test = opcua_client.ProtocolAttribute(m_node, j[0], 0, opcua_client.numpy_to_OPCua_dict[i.numpy_type], self.event_loop)
+                    test = opcua_client.ProtocolAttribute(m_node, j[0], 0, opcua_client.numpy_to_OPCua_dict[i.numpy_type], self.loop)
                 else:
-                    test = opcua_client.ProtocolAttribute(m_node, j[1], j[0], opcua_client.numpy_to_OPCua_dict[i.numpy_type], self.event_loop)
+                    test = opcua_client.ProtocolAttribute(m_node, j[1], j[0], opcua_client.numpy_to_OPCua_dict[i.numpy_type], self.loop)
 
                 # comparison function that replaces `set_data_value` inside the attributes write function
                 async def compare_values(val):
@@ -278,4 +258,4 @@ class TestOPCua(base.TestCase):
                 m_node.set_data_value = compare_values
 
                 # call the write function with the test values
-                test.write_function(get_test_value())
+                await test.write_function(get_test_value())