简体   繁体   中英

Proper structure for many test cases in Python with unittest

I am looking into the unittest package, and I'm not sure of the proper way to structure my test cases when writing a lot of them for the same method. Say I have a fact function which calculates the factorial of a number; would this testing file be OK?

import unittest

class functions_tester(unittest.TestCase):
    def test_fact_1(self):
        self.assertEqual(1, fact(1))
    def test_fact_2(self):
        self.assertEqual(2, fact(2))
    def test_fact_3(self):
        self.assertEqual(6, fact(3))
    def test_fact_4(self):
        self.assertEqual(24, fact(4))
    def test_fact_5(self):
        self.assertFalse(1==fact(5))
    def test_fact_6(self):
        self.assertRaises(RuntimeError, fact, -1)
        #fact(-1)

if __name__ == "__main__":
    unittest.main()

It seems sloppy to have so many test methods for one method. I'd like to just have one testing method and put a ton of basic test cases (ie 4! ==24, 3!==6, 5!==120, and so on), but unittest doesn't let you do that.

What is the best way to structure a testing file in this scenario?

Thanks in advance for the help.

You can put the asserts in a loop:

def test_fact(self):
    tests = [(1,1), (2,2), (3,6), (4,24), (5,120)]
    for n,f in tests:
        self.assertEqual(fact(n), f)

I'd say your ways of doing it is generally fine (but read on).

You could, as interjay suggested, do a loop (and, incidentally, it only counts as one test because the unittest module counts the number of functions, not the number of asserts). But I assume you won't exhaustively try to test every number, or even all numbers within a very large interval. So looping won't save you much, and, especially in testing, you should aim at being explicit.

Having said that, you should test for a small number of subsequent numbers (say, 1 through 5), and then try to get a feel for where possible corner cases and failures points are. Say, test for 10, 100, 1000 (that is, change order of magnitude), negative numbers, zero etc.

BTW, watch out for your two last tests. The first of them doesn't mean much. fact(5) is different than a LOT of numbers (infinite numbers, actually). Test for the correct case, testing for the incorrect ones isn't productive.

def test_fact_5(self):
    self.assertFalse(1==fact(5))

The second one is badly named: "test_fact_6" makes me think you're testing fact(6). You should name it as something like "test_fact_minus_one", or at least "test_fact_negative_number".

def test_fact_6(self):
    self.assertRaises(RuntimeError, fact, -1)

Test naming is very important, both when you're debugging errors and when you refer back to the tests as documentation.

You've said you're looking into unittest , but consider using nose tests instead - they allow you to generate independant test cases programmatically.

How to generate dynamic (parametrized) unit tests in python? (answer)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM