I try to use unittest
for conference class, but I am not sure that I have considered all condition or more complex tests should be added?
conference.py
from datetime import timedelta, datetime
class Conference:
def __init__(self):
self.inputs ='input.txt'
self.outputs = 'output.txt'
self.talks = self.proposals_extract()
self.Track = 1
def proposals_extract(self):
talks = {}
f = open(self.inputs)
try:
while True:
line = f.readline()
duration = ''.join(filter(lambda i: i.isdigit(), line))
if duration:
talks[line] = duration
elif (not duration) and line:
print('for proposal {} no duration has been detected'.format(line))
break
if not line:
break
except FileNotFoundError as e:
raise
f.close()
return talks
def delete_content(self, pfile):
pfile.seek(0)
pfile.truncate()
def format_time(self, hours):
return (datetime.min + hours).strftime('%I:%M %p')
def plan(self, start, end, Output):
start = timedelta(hours=start)
end = timedelta(hours=end)
while start < end and self.talks:
item = self.talks.popitem()
duration = item[1]
proposal = item[0]
print(self.format_time(start), ' ', proposal)
Output.write('{} {} '.format(self.format_time(start), proposal))
start += timedelta(minutes=int(duration))
start = self.format_time(hours=start)
return start
def print_plan(self):
O = open('output.txt', 'a')
self.delete_content(O)
lunch = self.format_time(timedelta(hours=12))
while (self.talks):
print('Track {}\n'.format(self.Track))
O.write('Track {}\n'.format(self.Track))
end_morning_session = self.plan(9, 12, O)
print('{} {} '.format(lunch, ' Lunch\n'))
O.write('{} {} '.format(lunch, ' Lunch\n'))
end_afternoon_session =self.plan(13, 17, O)
print('{} {}'.format(end_afternoon_session, 'Networking Event\n'))
O.write('{} {}'.format(end_afternoon_session, 'Networking Event\n'))
self.Track += 1
O.close()
if __name__ == '__main__':
P = Conference()
P.print_plan()
TestConference.py
import unittest
import conference as c
from datetime import timedelta, datetime
import io
from unittest.mock import mock_open, patch
from mock_open.mocks import MockOpen, FileLikeMock
import os
try:
# pylint: disable=no-name-in-module
from unittest.mock import patch, call, NonCallableMock, DEFAULT
except ImportError:
from mock import patch, call, NonCallableMock, DEFAULT
class TestConference(unittest.TestCase):
@classmethod
def setUpClass(cls):
print('setupClass')
@classmethod
def tearDownClass(cls):
print('tearDownClass')
def setUp(self):
print('setUp')
f = open('input.txt')
self.lines={line.strip('') for line in f}
f.close()
def test_proposals_extract(self):
"""Check effects of reading from an empty file."""
myclass_instace = c.Conference()
handle = open(myclass_instace.inputs, 'r')
self.assertFalse(handle.closed)
self.assertEqual('input.txt', handle.name)
self.assertEqual('r', handle.mode)
self.assertEqual(0, handle.tell())
text = handle.read()
self.assertNotEqual(0, handle.tell())
self.assertNotEqual('', text)
handle.close()
self.assertTrue(handle.closed)
talks = myclass_instace.proposals_extract()
self.assertEqual(len(self.lines), len(talks))
"""Check calls made when `open` is used as a context manager."""
with open(myclass_instace.inputs, 'r') as handle:
self.assertFalse(handle.closed)
self.assertEqual(myclass_instace.inputs, handle.name)
self.assertEqual('r', handle.mode)
self.assertEqual(0, handle.tell())
def test_format_time(self):
myclass_instace = c.Conference()
time = myclass_instace.format_time(timedelta(hours=9))
self.assertEqual(time,'09:00 AM')
def test_plan(self):
O = open('output.txt', 'a')
myclass_instace = c.Conference()
end_session = myclass_instace.plan(9, 10, O)
self.assertEqual(end_session,'10:00 AM')
end_session = myclass_instace.plan(16, 17, O)
self.assertEqual(end_session,'05:00 PM')
O.close()
def test_print_plan(self):
myclass_instace = c.Conference()
myclass_instace.print_plan()
self.assertTrue(os.path.isfile(myclass_instace.outputs))
self.assertFalse(myclass_instace.talks)
if __name__ == '__main__':
unittest.main()
1 Answer 1
Parametric files
It would be trivially easy to have Conference
accept filenames as parameters to its constructor, and will make this more usable.
Variable names
Track
should be lower-case. O
is a poor choice of variable name for a list of reasons:
- It should be lowercase
- It looks the same as a zero
- Generally speaking, single-letter variable names are mystifying, and it's only by luck that I understand it to mean "output"
P
should similarly be called plan
.
Context management
Avoid open
ing f
and O
and close
ing them separately; put this in a with
instead. Delete your FileNotFoundError
block; it does nothing useful.
Line iteration
Replace
while True:
line = f.readline()
if not line:
break
with
for line in f:
Unpacking
Try replacing
duration = item[1]
proposal = item[0]
with
proposal, duration = item
assuming that there are only two items in this sequence.
Spelling
instace
= instance
.
Tests
This entire block:
myclass_instace = c.Conference()
handle = open(myclass_instace.inputs, 'r')
self.assertFalse(handle.closed)
self.assertEqual('input.txt', handle.name)
self.assertEqual('r', handle.mode)
self.assertEqual(0, handle.tell())
text = handle.read()
self.assertNotEqual(0, handle.tell())
self.assertNotEqual('', text)
handle.close()
self.assertTrue(handle.closed)
"""Check calls made when `open` is used as a context manager."""
with open(myclass_instace.inputs, 'r') as handle:
self.assertFalse(handle.closed)
self.assertEqual(myclass_instace.inputs, handle.name)
self.assertEqual('r', handle.mode)
self.assertEqual(0, handle.tell())
is not useful and should be deleted. You're basically testing that file IO in Python works, and spoiler alert: it does. The only valuable part of this test is when you call proposals_extract
and validate its return value against known-good data.