> And the current code in main.py:
>
> #!/usr/bin/env python3
>
> class Editor(object):
>     def __init__(self):
>         pass
>
>     def edit_classifiers(self):
>         pass
>
> if __name__ == "__main__":
>     main()
>
> At this moment I am stumped as to what sort of test to write for the
> method edit_classifiers().
>
> At this very beginning stage, what I would want to have happen is to
> call edit_classifiers, it will try to open classifiers.txt, realize
> that it does not exist yet, and offer to create this file for the user
> to start entering the needed grammar classifiers.


You can make the file input/output interface a parameter of your editor.

###############################
class Editor(object):
    def __init__(self, filesystem):
        self.filesystem = filesystem
    ...
################################

Since it's an explicit parameter, we can pass in either something that
does it "for real" by using the built-in input output functions, or we
can "fake it", by providing something that's convenient for unit
tests.


What do we need out of an input/output interface?  Well, maybe a few
basic operations.  Let's say that we need to be able to do two things:


    1. Open files, which returns a "filelike" object.  If it can't
find the file, let's have it raise an IOError.

    2. Create new files, which returns a "filelike" object.

This is admittedly bare-bones, but let's demonstrate what this might
look like.  First, let's see what a real implementation might look
like:

################################
class RealFilesystem(object):
    def __init__(self):
        pass

    def open(self, filename):
        return open(filename, 'r')

    def create(self, filename):
        return open(filename, 'w')
################################

where we're just delegating the methods here to use the built-in
open() function from Python's standard library.

If we need to construct an editor that works with the real file
system, that's not too bad:

    editor = Editor(filesystem=RealFilesystem())


Now what about a test-friendly version of this?  This actually isn't
bad either; we can make judicious use of the StringIO class, which
represents in-memory streams:

###############################################
from StringIO import StringIO

class FakeFilesystem(object):
    """Simulate a very simple filesystem."""
    def __init__(self):
        self.filecontents = {}

    def _open_as_stringio(self, filename):
        filelike = StringIO(self.filecontents[filename])
        real_close = filelike.close
        def wrapped_close():
            self.filecontents[filename] = filelike.getvalue()
            real_close()
        filelike.close = wrapped_close
        return filelike

    def open(self, filename):
        if filename in self.filecontents:
            return self._open_as_stringio(filename)
        else:
            raise IOError, "Not found"

    def create(self, filename):
        self.filecontents[filename] = None
        return self._open_as_stringio(filename)
################################################

(I'm using Python 2.7; if you're on Python 3, substitute the initial
import statement with "from io import StringIO").


This is a class that will look approximately like a filesystem,
because we can "create" and "open" files, and it'll remember.  All of
this is in-memory, taking advantage of the StringIO library.  The
"tricky" part about this is that we need to watch when files close
down, because then we have to record what the file looked like, so
that next time we open the file, we can recall it.


Let's see how this works:

################################
>>> fs = FakeFilesystem()
>>> fs.open('hello')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "fake_filesystem.py", line 21, in open
    raise IOError, "Not found"
IOError: Not found
>>> h = fs.create('hello')
>>> h.write('hello world')
>>> h.close()
>>> h2 = fs.open('hello')
>>> h2.read()
'hello world'
>>> h2.close()
################################

It will remember.  Good.


So for our own unit tests, now we should be able to say something like this:

##################################################
    def test_foobar(self):
        fs = FakeFileSystem()
        fs.filecontents['classifiers.txt'] = """
something here to test what happens when classifiers exists.
"""
        e = Editor(filesystem=fs)
        # ... fill me in!
##################################################


Our test cases can control the environmental conditions.  As long as
the editor uses the file system exclusively through that parameter,
we're now able to freely swap out the real filesystem with the fake
one.
_______________________________________________
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor

Reply via email to