How to emulate data from a serial port using Python 3 unittest mocks?

Viewed 5648

I want to create unit test for a Python 3 method that reads unicode text strings from a serial port. I want to test the method's response to various strings. The line of code that I want to emulate is:

comm_buffer = serial_port.readline().decode("utf-8").strip()

Where 'serial_port' is an instance of a serial port that is passed to the method. I would like to use the unittest.mock module to set the comm_buffer variable to a unicode string, but I have been struggling all day with no success. My first time with trying to use mock, and I'm out of my depth.

The code for the whole method is:

def wait_for_data(serial_port, comm_flag = ""):
"""Read lines of data from the serial_port

Receives optional comm_flag (single character to check for at beginning of string)"""
logging.debug("Start wait_for_data  " + comm_flag)
timeout_count = 0
while True:
    # Get a line from the buffer and convert to string and strip line feed
    logging.debug("Waiting for data…")
    comm_buffer = serial_port.readline().decode("utf-8").strip()
    if len(comm_buffer) == 0:
        timeout_count += 1
        logging.warning("Serial port timeout - no data received. Timeout count = " + str(timeout_count))
        if timeout_count > 10:
            raise TimeoutError(["Too many timeouts"])
    # If no id character was specified, just return the string
    elif comm_flag == "":
        logging.debug("Returning no comm_flag")
        return comm_buffer
    # If an id character was specified, return the string if it's present (strip id), otherwise wait for another string
    elif comm_buffer[0] == comm_flag:
        logging.debug("Returning with comm_flag")
        return comm_buffer[1:]
1 Answers

Serial_port is not an instance of a serial port but an object with a readline() method. So don’t care about such thing as a serial port, your mock object is an object with the readline() method which provides the kind of value you want to test. So you just have to crate something like :

port = Mock()
port.readline = Mock(return_value="my string")

This is the first parameter for your call. So, if I copy your function in a module named test.port this tests are ok :

class TestWaitData(unittest.TestCase):

    def testFunc(self):
        port = mock.Mock()
        port.readline = mock.Mock(return_value="my string".encode('utf-8')) # as you use a decode call

        self.assertEqual(test.port.wait_for_data(port), "my string")

    def testBufferEmpty(self):
        port = mock.Mock()
        port.readline = mock.Mock(return_value="".encode('utf-8'))

        with self.assertRaises(TimeoutError):
            test.port.wait_for_data(port)

    def testWithFlag(self):
        port = mock.Mock()
        port.readline = mock.Mock(return_value="fmy string".encode('utf-8'))

        self.assertEqual(test.port.wait_for_data(port, 'f'), "my string")
Related