Re: [Pyunit-interest] testing order of classes in a module
Brought to you by:
purcell
|
From: Andrew D. <ad...@mi...> - 2003-01-12 18:24:22
|
Steve Purcell:
> The framework relies on the test author making sure that individual test case
> do not interfere with each other's results. In your case, sorting the test
> cases by class name solves the problem, but I think the real difficulty is
> how you have defined the tests.
Yes, I think I do understand that philosophy.
> If your current test cases interfere with each other due to the stateful
> nature of the server, you should probably do one of the following things.
> Firstly, you could arrange that the server is reset after every test case by
> overriding the 'tearDown()' method of TestCase. Or secondly, you could merge
> interdependent test cases into single test cases, such that they run
> together.
I have no control of the server, so I can't do the first case. I could
merge the test cases together, but that doesn't feel right. I would be
mixing quite different tests together in a test case.
What I have are a set of independent test cases. If I run them in
"live" mode, I do not have a problem, no matter the ordering of
the test cases, nor even the order of the method calls in a test case.
(I say that last because I noticed that unittest does sort the methods
by name, so there is some enforcement of order -- just not one that
matters to me.)
On thinking about this, I realized the statefulness of the server
really wasn't the problem. Consider the following tests, which
is characteristic of what I'm doing.
import unittest, testsupport, sys, os, urllib2
opener = None
PICKLE_FILENAME = "test_websites.pickle"
class TestPythonOrg(unittest.TestCase):
def testMainPage(self):
infile = opener("http://www.python.org")
.... check for items on that page
def testDocsPage(self):
infile = opener("http://www.python.org/doc/")
.... check for items on that page
# Make sure the first hit from a goole search has
# the the search text in the description. Totally
# fake API.
class TestGoogle(unittest.TestCase):
def testSimpleSearch(self):
form1 = parse_form(opener("http://www.google.com/")
form1["q"] = "python"
infile = form1.submit(opener = opener)
results = parse_google_html_stream(infile)
assert "spam" in results.group[0].text.lower()
if __name__ == "__main__":
s = os.environ.get("TEST_SOURCE", "PICKLE")
if s == "PICKLE":
opener = testsupport.OpenFromPickle(PICKLE_FILENAME)
elif s == "LIVE":
opener = urllib2.build_opener()
elif s == "CREATE":
opener = testsupport.CreatePickle(PICKLE_FILENAME)
else:
raise TypeError("Unknown TEST_SOURCE: %r" % (s,))
unittest.main()
Notice that every single test is independent of the others.
Inside of a test there is state.
Now, it may be that some of these tests take a non-trivial
amount of time to run -- perhaps 10 seconds. These
add up, and it's part of the unittest testing philosophy
that these tests be fast. (as in
http://c2.com/cgi/wiki?OptimizingUnitTests )
I can agree with this. Getting validation in 5 seconds
total as compared to 2 minutes really makes a difference.
So what I did was provide something else which implements
the URL opener interface. This reads the requests from
a local file instead of calling the server. It's a lot
faster, but the implementation assumes that all the methods
are always called in the same order.
What I want could work with the unittest interface if
I told each method how to get the stored data for that
specific method. Eg, here's one possible API
def testSimpleSearch(self):
opener = picklestore.fetch_for("testSimpleSearch")
form1 = opener("http://www.google.com/")
...
if __name__ == "__main__":
if "PICKLE", picklestore = support.ReadPickleStore()
if "LIVE", picklestore = support.NoPickleStore()
if "CREATE", picklestore = support.CreatePickleStore()
However, that makes the code a bit more complicated,
since not only do I need that extra line, but I have to
remember to name each test differently, even across all
unit test instances. (I considered inspecting the stack
to get the method and class name automatically, but that
didn't work because some of the methods are shared in a
base class.)
There's some minor implementation annoyances too. Eg,
I would either need random access to a named pickle string
from a (akin to what the dbms give me .. hmmm..) or I
need to read the whole data set into an in-memory dict.
(only about 200K, so I guess that isn't too bad either.)
Rather than do all that, I decided to try the simplest
thing possible, and make the test cases be ordered. That
was about 5-10 minutes vs. an hour or more of coding.
Andrew
da...@da...
|