I am trying to mock a class which is used as a context manager that makes network calls. Specifically, it's Read method returns the value of a PLC's tag over the network.
Here is an example of the function I am trying to test in main.py
:
from pylogix import PLC
def read_counter(counter_entry):
with PLC() as comm:
count = comm.Read(counter_entry['tag'])
if count.Status == 'Success':
log_count(count.Value)
counter_entry['last_count'] == count.Value
My test looks like this:
import unittest
from unittest.mock import Mock, patch, MagicMock
from random import randint
import main
class MockComm():
def __init__(self):
self.tag_dict = {}
def __enter__(self):
print('__enter__ called')
return self
def __exit__(self):
print('__exit__ called')
def add(self, tag, value):
print('Added ', tag, ' as ', value)
self.tag_dict[tag] = value
def Read(self, tag):
print('Reading ', tag)
if tag in self.tag_dict:
print('returned ', self.tag_dict[tag])
return Response(tag, Value=self.tag_dict[tag])
else:
print('returned Connection Failure')
return Response(tag, Value=None, Status='Connection failure')
class Response():
def __init__(self, tag, Value=None, Status='Success'):
self.TagName = tag
self.Value = Value
self.Status = Status
class ReadPylogixCounterTestSuit(unittest.TestCase):
def setUp(self):
self.counter_entry = {
'type': 'pylogix_counter',
'tag': 'Program:Production.DailyTotal',
'Part_Type_Tag': 'Stn010.PartType',
'Part_Type_Map': {'0': '50-4865', '1': '50-5081'},
# used internally to track the readings
'nextread': 0, # timestamp of the next reading
'lastcount': 0, # last counter value
'lastread': 0 # timestamp of the last read
}
@patch('main.PLC', new_callable=MockComm)
def test_read_tag(self, mock_PLC):
FIRST_COUNTER_VALUE = randint(1, 2500)
mock_PLC.add(self.counter_entry['tag'], FIRST_COUNTER_VALUE)
mock_PLC.add(self.counter_entry['Part_Type_Tag'], 0)
main.read_counter(self.counter_entry)
assert self.counter_entry['lastcount'] == FIRST_COUNTER_VALUE
EDIT: Update the code in the question to be a complete runnable example.
When I step through the above code, mock_PLC is an instance of Mock_Comm. As soon as I hit the with PLC() as comm
line I get the following traceback:
=====================================================================
ERROR: test_read_tag (test_read_counter.ReadPylogixCounterTestSuit)
----------------------------------------------------------------------
Traceback (most recent call last):
File "C:\Users\cstrutto\AppData\Local\Programs\Python\Python37\lib\unittest\mock.py", line 1256, in patched
return func(*args, **keywargs)
File "c:\programing\python-testing\test_context_manager\test_read_counter.py", line 81, in test_read_tag
main.read_counter(self.counter_entry)
File "c:\programing\python-testing\test_context_manager\main.py", line 4, in read_counter
with PLC() as comm:
TypeError: 'MockComm' object is not callable
----------------------------------------------------------------------
Ran 1 test in 34.908s
FAILED (errors=1)
I have the required __enter__
and __exit__
methods. Why is it not working as a context manager?