So I’m writing a test for this class (edited to be more clear):
class SpreadSheet(object): '''awesome docstring''' def __init__(self, filename): self.filename = filename self.table =  self.headers =  with open(self.filename) as csvfile: filereader = reader(csvfile, delimiter=',') for row in filereader: self.table.append(row) def create_headers(self, populations): ...code... def lookup_header(self, ltr): ...code... def write_header(self, targetfile): ...code...
that so far looks like this:
class TestSpreadSheet(unittest.TestCase): @contextmanager def make_fake_csv(self, data): self.fake_namefile = tempfile.NamedTemporaryFile(delete=False) with open(self.fake_namefile, 'w') as fake_csv: fake_writer = csv.writer(fake_csv) fake_writer.writerows(data) yield self.fake_namefile.name os.unlink(self.fake_namefile.name) def setUp(self): self.headers =  self.table = [ ['Col1', 'Col2', 'Col3', 'Col4', 'Col5', 'Col6', 'Col7', 'Col8'], ['val1', 'val2', 'val3', 'val4', 'val5', 'val6', 'val7', 'val8'], ['val1', 'val2', 'val3', 'val4', 'val5', 'val6', 'val7', 'val8'], ['val1', 'val2', 'val3', 'val4', 'val5', 'val6', 'val7', 'val8']] def test___init__(self): with self.make_fake_csv(self.table) as temp_csv: spread_sheet = SpreadSheet(temp_csv) self.assertEqual( self.table, spread_sheet.table) ...tests for other functions...
And I get this error:
in make_fake_csv with open(self.fake_namefile, 'w') as fake_csv: TypeError: coercing to Unicode: need string or buffer, instance found
I’ve scoured many other topics like this that point to the use of
tempfile to make a named object or something that can actually be called using
with open.... And while I did actually get that to work, my issues was when I tried to use the
csv package to format my
self.table for me into a csv formatted raw “string” (like the raw input of a csv file in other words).
Any pointers on how I can test this differently or make the current code work? Again I’m trying to:
- figure out how to use
csvto do all the formatting heavy-lifting to load up a fake csv file from my
self.tableso that I don’t have to make a huge string formatting expression
- make sure that the fake file works with
with openas used in my original class
SpreadSheetwhen the test is run
- can be used further to run the test of the other functions because they too need to instantiate
SpreadSheetwith a file in order to perform their functions.
And as a side question, is it “leaner” to make a fake “memory” file to do things like this (this is what I’m attempting above) or is is just simpler to make an actual temporary file on the disk and load it up during testing and use a
tearDown() function to delete it?
self.fake_namefile in your example is an instance of
NamedTemporaryFile. When you do the
open() call you need to pass a string containing the file name, not a
NamedTemporaryFile instance. The name of the temporary file is available in the
with open(self.fake_namefile.name, 'w') as fake_csv:
Here’s some suggestions:
- Let your Spreadsheet class take a file-like object rather than a filename. This makes it more generic and allows it to be used with other stream based objects. If you had this, there’d be no need to create a fake file, you could simply construct a
StringIOinstance for testing.
- If you’re set on using a
NamedTemporaryFile, I’d suggest to use it as a context manager directly as outlined in the other answer.
You don’t have to use the
NamedTemporaryFile. Instead, wrap your entire test in the context manager as follows.
def test_stuff(self): with tempfile.NamedTemporaryFile() as temp_csv: self.write_csv_test_data(temp_csv) # Create this to write to temp_csv file object. temp_csv.flush() temp_csv.seek(0)
spread_sheet = SpreadSheet(temp_csv.name) # spread_sheet = SpreadSheet(temp_csv) Use this if Spreadsheet takes a file-like object ...
Here’s an example using only file-like objects, there’s no disk file involved.
class SpreadSheet(object): '''awesome docstring''' def __init__(self, fileobj): self.table =  self.headers =  filereader = reader(fileobj, delimiter=',') for row in filereader: self.table.append(row) ...
It could then be used like this, assuming you were reading from a disk file:
with open(path) as csv_file: spreadsheet = Spreadsheet(csv_file) ....
And during testing, you can use the StringIO module to simulate a file on disk. The test then runs with data entirely in memory so is very fast.
import StringIO class TestSpreadSheet(unittest.TestCase): def make_fake_csv(self, data): """Return a populdated fake csv file object for testing.""" fake_csv = StringIO.StringIO() fake_writer = csv.writer(fake_csv) fake_writer.writerows(data) fake_csv.seek(0) return fake_csv .... def test___init__(self): temp_csv = self.make_fake_csv(self.table) spread_sheet = SpreadSheet(temp_csv) self.assertEqual( self.table, spread_sheet.table)