473,616 Members | 2,970 Online
Bytes | Software Development & Data Engineering Community
+ Post

Home Posts Topics Members FAQ

Unittest - How do I code lots of simple tests

One of the things I really dislike about Unittest (compared, say, to a
number of adhoc testing tricks I've used in the past, and to Perl's
"standard" testing framework) is that the testcase-as-a-class model
tends to imply a relatively high granularity in testing.

A good example of this comes from "Dive Into Python"
(http://diveintopython.org) section 7.3. Here, the author has written
a module which converts numbers to Roman numerals. The test case is
then

class KnownValues(uni ttest.TestCase) :
knownValues = ( (1, 'I'), ... # 50-plus specific cases

def testToRomanKnow nValues(self):
"""toRoman should give known result with known input"""
for integer, numeral in self.knownValue s:
result = roman.toRoman(i nteger)
self.assertEqua l(numeral, result)

Now, to my mind, the big problem here is that this *isn't* one test,
but rather 50 (or more). OK, the same checks are done if everything
succeeds, but

1. If a test fails, the rest are skipped! If there's a pattern to the
failures (the code handles numbers ending in 4 and 9 wrongly, for
example) it's much easier to find if all of the checks are
reported.
2. Psychologically , "52 tests succeeded" is a much bigger boost than
"1 test succeeded" (albeit this test covered 52 cases). And it's
not just psychology - we really *did* test 52 distinct conditions.

The only way I can see of producing the "true" number of tests is via
some form of ugly hack like

test_values = ((1, 'I'), ...)

class KnownValues(uni ttest.TestCase) :
pass

for arabic, roman in test_values:
def test(self):
result = roman.toRoman(a rabic)
self.assertEqua l(roman, result)
setattr(KnownVa lues, 'test_%s_%s' % (arabic, roman), test)

But I can't really see that as the "right approach".

Can anyone suggest a more reasonable way of running this sort of
table-driven test via unittest?

Maybe I should use doctest instead, but to be honest, I prefer the
overall infrastructure of unittest (for real tests). It's just this
particular issue that really bugs me...

Paul.
--
This signature intentionally left blank
Jul 18 '05 #1
12 2575
Paul Moore wrote:

Can anyone suggest a more reasonable way of running this sort of
table-driven test via unittest?


Why not just extend self.assertEqua l() and use your own check, with
additional logic as required to increment counters or add items
to the list of passing tests. Then put a final check of the number
of passing tests or something like that at the end to make sure
things worked overall.
For example:

class KnownValues(uni ttest.TestCase) :
def setUp(self):
self.passCount = 0

def checkValue(self , expected, result):
if expected == result:
self.passCount += 1
else:
# left as exercise to the reader, but pass would work...

def testToRomanKnow nValues(self):
for integer, numeral in self.knownValue s:
result = roman.toRoman(i nteger)
self.checkValue (numeral, result)
self.assertEqua l(len(self.know nValues), self.passCount)

No, you don't get the psychologically affirming "52 tests passed!"
without changes to the TestRunner, but I assume the non-cosmetic part
of this is more your concern right now...

-Peter
Jul 18 '05 #2
Paul Moore wrote:
Can anyone suggest a more reasonable way of running this sort of
table-driven test via unittest?

Take a look at Docutils' test/DocutilsTestSup port.py for ideas. The
CustomTestSuite and CustomTestCase classes provide support for named
data-driven tests. Most of Docutils' tests are data-driven.

Ian Bicking wrote: unittest is not written with subclassing in mind, except for the
limited subclassing that is documented. (And it uses
double-underscore variables, like it's just *trying* to piss me off!
Double-underscore variables are so arrogant and patronizing.


All very true. Double-underscores ought to be banned from the
standard library. They inevitably get in the way because no matter
how well a class is written, somebody is going to want to subclass it
in a way the original author never considered.

--
David Goodger http://starship.python.net/~goodger
For hire: http://starship.python.net/~goodger/cv
Docutils: http://docutils.sourceforge.net/
(includes reStructuredTex t: http://docutils.sf.net/rst.html)
Jul 18 '05 #3
Paul Moore <pf******@yahoo .co.uk> wrote in message news:<br******* ***@yahoo.co.uk >...
1. If a test fails, the rest are skipped! If there's a pattern to the
failures (the code handles numbers ending in 4 and 9 wrongly, for
example) it's much easier to find if all of the checks are
reported.


That's true, but most of the time when I test, I prefer that behavior.
Many of my asserts depend on the success of the assert prior to
them, so I want the test to fail as soon as one of the asserts has
failed.

It'd be nice to have both possibilities, though.

Jeremy
Jul 18 '05 #4
Hello Paul,
test_values = ((1, 'I'), ...)

class KnownValues(uni ttest.TestCase) :
pass

for arabic, roman in test_values:
def test(self):
result = roman.toRoman(a rabic)
self.assertEqua l(roman, result)
setattr(KnownVa lues, 'test_%s_%s' % (arabic, roman), test)

But I can't really see that as the "right approach".

On reason that it won't do what you want :-)
All the tests will check the last value in test_values. (Something to
do with binding rules, can't recall how to solve)

Try:
--- i2r.py ---
#!/usr/bin/env python
from unittest import TestCase, makeSuite, main

class Roman:
def toRoman(self, i):
return { 1 : "I",
2 : "II",
3 : "III",
4 : "IV",
5 : "V"}[i]
roman = Roman()

class KnownValues(Tes tCase):
pass

test_values = ((1, "I"), (2, "II"), (3, "III"), (4, "IV"), (5, "V"))
for a, r in test_values:
def test(self):
print a, r
result = roman.toRoman(a )
self.assertEqua l(r, result)
setattr(KnownVa lues, "test_%s_%s " % (a, r), test)

test_suite = makeSuite(Known Values, "test_")

if __name__ == "__main__":
main()
--- i2r.py ---

$ python i2r.py
5 V
..5 V
..5 V
..5 V
..5 V
..
----------------------------------------------------------------------
Ran 5 tests in 0.000s

OK
HTH.
Miki
Jul 18 '05 #5
Paul Moore <pf******@yahoo .co.uk> wrote in news:br******** **@yahoo.co.uk:
But I can't really see that as the "right approach".

Can anyone suggest a more reasonable way of running this sort of
table-driven test via unittest?


Ok, how about the file below.
It uses a metaclass to generate dynamic tests from a table. I deliberately
wrote the tests so that one fails, when run it will tell you that
test_roman_v failed so you can see that the names get handled properly.

Output:
D:\temp>tablete st.py --verbose
testMe (__main__.MyTes ts) ... ok
test_roman_i (__main__.MyTes ts) ... ok
test_roman_v (__main__.MyTes ts) ... FAIL
test_roman_x (__main__.MyTes ts) ... ok

=============== =============== =============== =============== ==========
FAIL: test_roman_v (__main__.MyTes ts)
----------------------------------------------------------------------
Traceback (most recent call last):
File "D:\temp\tablet est.py", line 21, in test
self.assertEqua ls(roman, 'x')
File "D:\Python23\li b\unittest.py", line 302, in failUnlessEqual
raise self.failureExc eption, \
AssertionError: 'v' != 'x'

----------------------------------------------------------------------
Ran 4 tests in 0.020s

FAILED (failures=1)
Note that the metaclass itself is reusable. The factory
function tableDrivenTest s, although defined within the class is a function,
not a method, and cannot access any members of the class (since the class
does not yet exist at the time it is called). The table itself either has
to be created inside the tableDrivenTest s function, or global.
The factory function simply returns a dictionary of functions which are
added to the class, note that the keyname in the dictionary is important so
far as the unittest code is concerned, not the original name of the
function.

Also be sure to pass parameters into the test function using default
parameters as nested scopes will get the values left at the end of the loop
(so you might end up with lots of tests that all do the same thing). I
mention that here because I did exactly that writing the code.

---- begin tabletest.py ----
class MetaTableTest(t ype):
def __new__(metacls , name, bases, dict):
factory = dict['tableDrivenTes ts']
dict.update(fac tory())
return super(MetaTable Test, metacls).__new_ _(metacls, name, bases,
dict)

import unittest

class MyTests(unittes t.TestCase):
__metaclass__ = MetaTableTest
def tableDrivenTest s():
'''Return a dictionary of additional test functions'''
knownValues = (1,'i'), (5, 'v'), (10, 'x')
table = {}
for arabic, roman in knownValues:
def test(self, arabic=arabic, roman=roman):
if arabic==1:
self.assertEqua ls(roman, 'i')
else:
self.assertEqua ls(roman, 'x')

table['test_roman_%s' % roman] = test
return table

def testMe(self):
self.assert_(Tr ue)
if __name__=='__ma in__':
unittest.main()

---- end tabletest.py ----

--
Duncan Booth du****@rcp.co.u k
int month(char *p){return(1248 64/((p[0]+p[1]-p[2]&0x1f)+1)%12 )["\5\x8\3"
"\6\7\xb\1\x9\x a\2\0\4"];} // Who said my code was obscure?
Jul 18 '05 #6
Miki Tebeka wrote:
Hello Paul,
test_values = ((1, 'I'), ...)

class KnownValues(uni ttest.TestCase) :
pass

for arabic, roman in test_values:
def test(self):
result = roman.toRoman(a rabic)
self.assertEqua l(roman, result)
setattr(KnownVa lues, 'test_%s_%s' % (arabic, roman), test)

But I can't really see that as the "right approach". On reason that it won't do what you want :-)
All the tests will check the last value in test_values. (Something to
do with binding rules, can't recall how to solve)

Try:
--- i2r.py ---
#!/usr/bin/env python
from unittest import TestCase, makeSuite, main

class Roman:
def toRoman(self, i):
return { 1 : "I",
2 : "II",
3 : "III",
4 : "IV",
5 : "V"}[i]
roman = Roman()

class KnownValues(Tes tCase):
pass

test_values = ((1, "I"), (2, "II"), (3, "III"), (4, "IV"), (5, "V"))
for a, r in test_values:
def test(self):


Change the above line to

def test(self, a=a, r=r):

or you will perform the test five times with (5, "V").
print a, r
result = roman.toRoman(a )
self.assertEqua l(r, result)
setattr(KnownVa lues, "test_%s_%s " % (a, r), test)

test_suite = makeSuite(Known Values, "test_")

if __name__ == "__main__":
main()
--- i2r.py ---

I like the idea, once the little error is removed. In general, I think the
unit test code should be as simple as possible. Otherwise we would need
unit tests for unit tests for...

Peter

Jul 18 '05 #7
Peter Hansen <pe***@engcorp. com> wrote in message news:<3F******* ********@engcor p.com>...
Paul Moore wrote:

Can anyone suggest a more reasonable way of running this sort of
table-driven test via unittest?


Why not just extend self.assertEqua l() and use your own check, with
additional logic as required to increment counters or add items
to the list of passing tests. Then put a final check of the number
of passing tests or something like that at the end to make sure
things worked overall.
For example:

class KnownValues(uni ttest.TestCase) :
def setUp(self):
self.passCount = 0

def checkValue(self , expected, result):
if expected == result:
self.passCount += 1
else:
# left as exercise to the reader, but pass would work...

def testToRomanKnow nValues(self):
for integer, numeral in self.knownValue s:
result = roman.toRoman(i nteger)
self.checkValue (numeral, result)
self.assertEqua l(len(self.know nValues), self.passCount)

No, you don't get the psychologically affirming "52 tests passed!"
without changes to the TestRunner, but I assume the non-cosmetic part
of this is more your concern right now...


I don't see the remainder of the problem as "non-cosmetic". The error
report I get (or rather the information it offers) is

1 test failed - pass count is 25 instead of 52.

But that doesn't tell me *which* tests failed.

The key point here is that I'm NOT running one test - I really am
running 52 distinct tests. OK, they are all very similar, differing
only in the data - but for pity's sake, isn't that what an object
oriented structure is supposed to make easy???

Paul.
Jul 18 '05 #8
David Goodger <go*****@python .org> wrote in message news:<ma******* *************** *************** @python.org>...
Paul Moore wrote:
>>> Can anyone suggest a more reasonable way of running this sort of
>>> table-driven test via unittest?


Take a look at Docutils' test/DocutilsTestSup port.py for ideas. The
CustomTestSuite and CustomTestCase classes provide support for named
data-driven tests. Most of Docutils' tests are data-driven.


Urk. That's hairy stuff. Thanks for the pointer, I'll do some research.

But I still think that this sort of thing should be easy :-(

Paul.
Jul 18 '05 #9
At 3:11 AM -0700 10/22/03, Paul Moore wrote:
Peter Hansen <pe***@engcorp. com> wrote in message
news:<3F****** *********@engco rp.com>...
> > No, you don't get the psychologically affirming "52 tests passed!" without changes to the TestRunner, but I assume the non-cosmetic part
of this is more your concern right now...


I don't see the remainder of the problem as "non-cosmetic". The error
report I get (or rather the information it offers) is

1 test failed - pass count is 25 instead of 52.

But that doesn't tell me *which* tests failed.


Are you running the tests verbosely? eg. with a -v argument under
UNIX, or as specified in the docs
<http://www.python.org/doc/current/lib/minimal-example.html>? I get
the following output when using Miki's code (with a deliberate error
thrown in, and the print statement commented out):

bash-2.05b$ ./test.py -v
test_1_I (__main__.Known Values) ... ok
test_2_II (__main__.Known Values) ... ok
test_3_II (__main__.Known Values) ... FAIL
test_4_IV (__main__.Known Values) ... ok
test_5_V (__main__.Known Values) ... ok

=============== =============== =============== =============== ==========
FAIL: test_3_II (__main__.Known Values)
----------------------------------------------------------------------
Traceback (most recent call last):
File "./test.py", line 21, in test
self.assertEqua l(r, result)
File "/usr/local/lib/python2.3/unittest.py", line 292, in failUnlessEqual
raise self.failureExc eption, \
AssertionError: 'II' != 'III'

----------------------------------------------------------------------
Ran 5 tests in 0.055s

FAILED (failures=1)
bash-2.05b$
The key point here is that I'm NOT running one test - I really am
running 52 distinct tests. OK, they are all very similar, differing
only in the data - but for pity's sake, isn't that what an object
oriented structure is supposed to make easy???


Well, you're testing one aspect of the code. It's really just a
question of how you think about your tests.

Anthony
--
----------------------------------------------------
HyPEraCtiVE? HeY, WhO aRE YoU cALliNg HypERaCtIve?!
aB*****@wEStNeT .cOm.aU
----------------------------------------------------

Jul 18 '05 #10

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

3
1464
by: Richard Wesley | last post by:
Hi all - I am trying to retrofit some units test into our code using the unittest module. I can get individual rewrites to work, but I am having trouble writing the "one test to run them all and in the darkness bind them" script. There are two main sources of trouble: - Test data that is stored next to the script that uses it; - Several levels of scripts (top/utils/tests) with tests/data scattered at all levels;
3
1913
by: Gonçalo Rodrigues | last post by:
Hi, I use the unittest module for testing a given module's functionality, by lumping several test classes and then at the end a simple if __name__ == "__main__": unittest.main() In one of these, I get, at a DOS prompt the following weird result:
0
2039
by: Remy Blank | last post by:
Ok, here we go. I added the possibility for tests using the unittest.py framework to be skipped. Basically, I added two methods to TestCase: TestCase.skip(msg): skips unconditionally TestCase.skipIf(expr, msg): skips if expr is true These can be called either in setUp() or in the test methods. I also added reporting of skipped tests to TestResult, _TextTestResult and
41
10262
by: Roy Smith | last post by:
I've used the standard unittest (pyunit) module on a few projects in the past and have always thought it basicly worked fine but was just a little too complicated for what it did. I'm starting a new project now and I'm thinking of trying py.test (http://codespeak.net/py/current/doc/test.html). It looks pretty cool from the docs. Is there anybody out there who has used both packages and can give a comparative review?
7
2065
by: Jorgen Grahn | last post by:
I have a set of tests in different modules: test_foo.py, test_bar.py and so on. All of these use the simplest possible internal layout: a number of classes containing test*() methods, and the good old lines at the end: if __name__ == "__main__": unittest.main() This is great, because each of the modules are runnable, and I can select classes or tests to run on the commandline if I want to. However, running all the tests from e.g. a...
3
3424
by: David Vincent | last post by:
-----BEGIN PGP SIGNED MESSAGE----- Hello I'm hoping to get some insight into a situation that seems odd to me. My Python experience is limited; I've just started using the unittest module. I've had some experience with unit test support in other languages.
0
2304
by: Chris Fonnesbeck | last post by:
I have built the following unit test, observing the examples laid out in the python docs: class testMCMC(unittest.TestCase): def setUp(self): # Create an instance of the sampler self.sampler = DisasterSampler()
1
3936
by: Chris Fonnesbeck | last post by:
I have a module for which I am trying to code a unit test. However, when I run unittest.main(), I get: In : import PyMC In : PyMC.unittest.main() ---------------------------------------------------------------------- Ran 0 tests in 0.000s
3
1971
by: Paul Moore | last post by:
My normal testing consists of a tests.py script using unittest, with the basic if __name__ == '__main__': unittest.main() incantation to get things going. But I now want to incorporate some other tests (specifically, a text file containing doctests) and I find that there is no gradual process
0
8203
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However, people are often confused as to whether an ONU can Work As a Router. In this blog post, we’ll explore What is ONU, What Is Router, ONU & Router’s main usage, and What is the difference between ONU and Router. Let’s take a closer look ! Part I. Meaning of...
1
8297
by: Hystou | last post by:
Overview: Windows 11 and 10 have less user interface control over operating system update behaviour than previous versions of Windows. In Windows 11 and 10, there is no way to turn off the Windows Update option using the Control Panel or Settings app; it automatically checks for updates and installs any it finds, whether you like it or not. For most users, this new feature is actually very convenient. If you want to control the update process,...
0
7121
agi2029
by: agi2029 | last post by:
Let's talk about the concept of autonomous AI software engineers and no-code agents. These AIs are designed to manage the entire lifecycle of a software development project—planning, coding, testing, and deployment—without human intervention. Imagine an AI that can take a project description, break it down, write the code, debug it, and then launch it, all on its own.... Now, this would greatly impact the work of software developers. The idea...
1
6097
isladogs
by: isladogs | last post by:
The next Access Europe User Group meeting will be on Wednesday 1 May 2024 starting at 18:00 UK time (6PM UTC+1) and finishing by 19:30 (7.30PM). In this session, we are pleased to welcome a new presenter, Adolph Dupré who will be discussing some powerful techniques for using class modules. He will explain when you may want to use classes instead of User Defined Types (UDT). For example, to manage the data in unbound forms. Adolph will...
0
5550
by: conductexam | last post by:
I have .net C# application in which I am extracting data from word file and save it in database particularly. To store word all data as it is I am converting the whole word file firstly in HTML and then checking html paragraph one by one. At the time of converting from word file to html my equations which are in the word document file was convert into image. Globals.ThisAddIn.Application.ActiveDocument.Select();...
0
4063
by: TSSRALBI | last post by:
Hello I'm a network technician in training and I need your help. I am currently learning how to create and manage the different types of VPNs and I have a question about LAN-to-LAN VPNs. The last exercise I practiced was to create a LAN-to-LAN VPN between two Pfsense firewalls, by using IPSEC protocols. I succeeded, with both firewalls in the same network. But I'm wondering if it's possible to do the same thing, with 2 Pfsense firewalls...
0
4141
by: adsilva | last post by:
A Windows Forms form does not have the event Unload, like VB6. What one acts like?
1
2579
by: 6302768590 | last post by:
Hai team i want code for transfer the data from one system to another through IP address by using C# our system has to for every 5mins then we have to update the data what the data is updated we have to send another system
0
1445
bsmnconsultancy
by: bsmnconsultancy | last post by:
In today's digital era, a well-designed website is crucial for businesses looking to succeed. Whether you're a small business owner or a large corporation in Toronto, having a strong online presence can significantly impact your brand's success. BSMN Consultancy, a leader in Website Development in Toronto offers valuable insights into creating effective websites that not only look great but also perform exceptionally well. In this comprehensive...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.