📄 sect03.htm
字号:
<font color=#0000ff>if</font> __name__ == <font color=#004488>"__main__"</font>:
os.path.walk('.', visitor, None)
#:~</PRE></FONT></BLOCKQUOTE><DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Just run this from the root
directory of the code listings for the book; it will descend into each
subdirectory and run the program there. An easy way to check things is to
redirect standard output to a file, then if there are any errors they will be
the only thing that appears at the console during program execution.
<A HREF="http://www.mindview.net/Books/TIPython/BackTalk/FindPage/A_92">Add Comment</A></FONT><A NAME="_Toc534420074"></A><BR></P></DIV>
<A NAME="Heading25"></A><FONT FACE = "Verdana, Tahoma, Arial, Helvetica, Sans"><H2 ALIGN="LEFT">
A very simple framework</H2></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">As mentioned, a primary goal of this code
is to make the writing of unit testing code very simple, even simpler than with
JUnit. As further needs are discovered <I>during the use</I> of this system,
then that functionality can be added, but to start with the framework will just
provide a way to easily create and run tests, and report failure if something
breaks (success will produce no results other than normal output that may occur
during the running of the test). My intended use of this framework is in
makefiles, and <B>make</B> aborts if there is a non-zero return value from the
execution of a command. The build process will consist of compilation of the
programs and execution of unit tests, and if <B>make</B> gets all the way
through successfully then the system will be validated, otherwise it will abort
at the place of failure. The error messages will report the test that failed but
not much else, so that you can provide whatever granularity that you need by
writing as many tests as you want, each one covering as much or as little as you
find necessary.
<A HREF="http://www.mindview.net/Books/TIPython/BackTalk/FindPage/A_93">Add Comment</A></FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">In some sense, this framework provides an
alternative place for all those “print” statements I’ve
written and later erased over the years.
<A HREF="http://www.mindview.net/Books/TIPython/BackTalk/FindPage/A_94">Add Comment</A></FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">To create a set of tests, you start by
making a <B>static </B> inner class inside the class you wish to test (your test
code may also test other classes; it’s up to you). This test code is
distinguished by inheriting from <B>UnitTest</B>:
<A HREF="http://www.mindview.net/Books/TIPython/BackTalk/FindPage/A_95">Add Comment</A></FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE># test:UnitTest.py
# The basic unit testing <font color=#0000ff>class</font>
<font color=#0000ff>class</font> UnitTest:
static String testID
static List errors = ArrayList()
# Override cleanup() <font color=#0000ff>if</font> test object
# creation allocates non-memory
# resources that must be cleaned up:
<font color=#0000ff>def</font> cleanup(self):
# Verify the truth of a condition:
protected final void affirm(boolean condition){
<font color=#0000ff>if</font>(!condition)
errors.add(<font color=#004488>"failed: "</font> + testID)
# :~</PRE></FONT></BLOCKQUOTE><DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The only testing method [[ So
far ]] is
<B>affirm( )</B></FONT><A NAME="fnB12" HREF="#fn12">[12]</A><FONT FACE="Georgia">,
which is <B>protected</B> so that it can be used from the inheriting class. All
this method does is verify that something is <B>true</B>. If not, it adds an
error to the list, reporting that the current test (established by the <B>static
testID</B>, which is set by the test-running program that you shall see shortly)
has failed. Although this is not a lot of information—you might also wish
to have the line number, which could be extracted from an exception—it may
be enough for most situations.
<A HREF="http://www.mindview.net/Books/TIPython/BackTalk/FindPage/A_96">Add Comment</A></FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Unlike JUnit (which uses
<B>setUp( )</B> and <B>tearDown( )</B> methods), test objects will be
built using ordinary Python construction. You define the test objects by
creating them as ordinary class members of the test class, and a new test class
object will be created for each test method (thus preventing any problems that
might occur from side effects between tests). Occasionally, the creation of a
test object will allocate non-memory resources, in which case you must override
<B>cleanup( )</B> to release those resources.
<A HREF="http://www.mindview.net/Books/TIPython/BackTalk/FindPage/A_97">Add Comment</A></FONT><A NAME="_Toc534420075"></A><BR></P></DIV>
<A NAME="Heading26"></A><FONT FACE = "Verdana, Tahoma, Arial, Helvetica, Sans"><H2 ALIGN="LEFT">
Writing tests</H2></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Writing tests becomes very simple.
Here’s an example that creates the necessary <B>static</B> inner class and
performs trivial tests:
<A HREF="http://www.mindview.net/Books/TIPython/BackTalk/FindPage/A_98">Add Comment</A></FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE># c02:TestDemo.py
# Creating a test
<font color=#0000ff>class</font> TestDemo:
private static int objCounter = 0
private int id = ++objCounter
public TestDemo(String s):
<font color=#0000ff>print</font> (s + <font color=#004488>": count = "</font> + id)
<font color=#0000ff>def</font> close(self):
<font color=#0000ff>print</font> (<font color=#004488>"Cleaning up: "</font> + id)
<font color=#0000ff>def</font> someCondition(self): <font color=#0000ff>return</font> 1
public static <font color=#0000ff>class</font> Test(UnitTest):
TestDemo test1 = TestDemo(<font color=#004488>"test1"</font>)
TestDemo test2 = TestDemo(<font color=#004488>"test2"</font>)
<font color=#0000ff>def</font> cleanup(self):
test2.close()
test1.close()
<font color=#0000ff>def</font> testA(self):
<font color=#0000ff>print</font> “TestDemo.testA“
affirm(test1.someCondition())
<font color=#0000ff>def</font> testB(self):
<font color=#0000ff>print</font> “TestDemo.testB“
affirm(test2.someCondition())
affirm(TestDemo.objCounter != 0)
# Causes the build to halt:
#! public void test3(): affirm(0)
# :~</PRE></FONT></BLOCKQUOTE><DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The <B>test3( )</B>
method is commented out because, as you’ll see, it causes the automatic
build of this book’s source-code tree to stop.
<A HREF="http://www.mindview.net/Books/TIPython/BackTalk/FindPage/A_99">Add Comment</A></FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">You can name your inner class anything
you’d like; the only important factor is that it <B>extends UnitTest</B>.
You can also include any necessary support code in other methods. Only <B>public
</B>methods that take no arguments and return <B>void</B> will be treated as
tests (the names of these methods are also not constrained).
<A HREF="http://www.mindview.net/Books/TIPython/BackTalk/FindPage/A_100">Add Comment</A></FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The above test class creates two
instances of <B>TestDemo</B>. The <B>TestDemo </B>constructor prints something,
so that we can see it being called. You could also define a default constructor
(the only kind that is used by the test framework), although none is necessary
here. The <B>TestDemo</B> class has a <B>close( )</B> method which suggests
it is used as part of object cleanup, so this is called in the overridden
<B>cleanup( )</B> method in <B>Test</B>.
<A HREF="http://www.mindview.net/Books/TIPython/BackTalk/FindPage/A_101">Add Comment</A></FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The testing methods use the
<B>affirm( )</B> method to validate expressions, and if there is a failure
the information is stored and printed after all the tests are run. Of course,
the <B>affirm( )</B> arguments are usually more complicated than this;
you’ll see more examples throughout the rest of this book.
<A HREF="http://www.mindview.net/Books/TIPython/BackTalk/FindPage/A_102">Add Comment</A></FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Notice that in <B>testB( )</B>, the
<B>private</B> field <B>objCounter</B> is accessible to the testing
code—this is because <B>Test</B> has the permissions of an inner class.
<A HREF="http://www.mindview.net/Books/TIPython/BackTalk/FindPage/A_103">Add Comment</A></FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">You can see that writing test code
requires very little extra effort, and no knowledge other than that used for
writing ordinary classes.
<A HREF="http://www.mindview.net/Books/TIPython/BackTalk/FindPage/A_104">Add Comment</A></FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">To run the tests, you use
<B>RunUnitTests.py</B> (which will be introduced shortly). The command for the
above code looks like this:
<A HREF="http://www.mindview.net/Books/TIPython/BackTalk/FindPage/A_105">Add Comment</A></FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia"><B>java com.bruceeckel.test.RunUnitTests
TestDemo</B></FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">It produces the following
output:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE>test1: count = 1
test2: count = 2
TestDemo.testA
Cleaning up: 2
Cleaning up: 1
test1: count = 3
test2: count = 4
TestDemo.testB
Cleaning up: 4
Cleaning up: 3</PRE></FONT></BLOCKQUOTE><DIV ALIGN="LEFT"><P><FONT FACE="Georgia">All the output is
noise as far as the success or failure of the unit testing is concerned. Only if
one or more of the unit tests fail does the program returns a non-zero value to
terminate the <B>make</B> process after the error messages are produced. Thus,
you can choose to produce output or not, as it suits your needs, and the test
class becomes a good place to put any printing code you might need—if you
do this, you tend to keep such code around rather than putting it in and
stripping it out as is typically done with tracing code.
<A HREF="http://www.mindview.net/Books/TIPython/BackTalk/FindPage/A_106">Add Comment</A></FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">If you need to add a test to a class
derived from one that already has a test class, it’s no problem, as you
can see here:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE># c02:TestDemo2.py
# Inheriting <font color=#0000ff>from</font> a <font color=#0000ff>class</font> that
# already has a test <font color=#0000ff>is</font> no problem.
<font color=#0000ff>class</font> TestDemo2(TestDemo):
public TestDemo2(String s): .__init__(s)
# You can even use the same name
# as the test <font color=#0000ff>class</font> <font color=#0000ff>in</font> the base <font color=#0000ff>class</font>:
public static <font color=#0000ff>class</font> Test(UnitTest):
<font color=#0000ff>def</font> testA(self):
<font color=#0000ff>print</font> “TestDemo2.testA“
affirm(1 + 1 == 2)
<font color=#0000ff>def</font> testB(self):
<font color=#0000ff>print</font> “TestDemo2.testB“
affirm(2 * 2 == 4)
# :~</PRE></FONT></BLOCKQUOTE><DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Even the name of the inner
class can be the same. In the above code, all the assertions are always true so
the tests will never fail.
<A HREF="http://www.mindview.net/Books/TIPython/BackTalk/FindPage/A_107">Add Comment</A></FONT><A NAME="_Toc534420076"></A><BR></P></DIV>
<A NAME="Heading27"></A><FONT FACE = "Verdana, Tahoma, Arial, Helvetica, Sans"><H2 ALIGN="LEFT">
White-box & black-box tests</H2></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The unit test examples so far are what
are traditionally called <I>white-box</I> <I>tests</I>. This means that the test
code has complete access to the internals of the class that’s being tested
(so it might be more appropriately called “transparent box”
testing). White-box testing happens automatically when you make the unit test
class as an inner class of the class being tested, since inner classes
automatically have access to all their outer class elements, even those that are
<B>private</B>.
<A HREF="http://www.mindview.net/Books/TIPython/BackTalk/FindPage/A_108">Add Comment</A></FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">A possibly more common form of testing is
<I>black-box testing</I>, which refers to treating the class under test as an
impenetrable box. You can’t see the internals; you can only access the
<B>public</B> portions of the class. Thus, black-box testing corresponds more
closely to functional testing, to verify the methods that the client programmer
is going to use. In addition, black-box testing provides a minimal instruction
sheet to the client programmer – in the absence of all other
documentation, the black-box tests at least demonstrate how to make basic calls
to the <B>public</B> class methods.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -