You may have heard about unittests in the past, and most likely, like myself, didn’t really know what they were or why you would want to use them. Even if you talked with someone who did use them and loved ’em, they still had a difficult time explaining what they really were.
Well I’ll do my best to answer these questions here:
First line of wikipedia says it all really: unittest
“In computer programming, unit testing is a method by which individual units of source code are tested to determine if they are fit for use.”
Basically these are short little bits of code you write to test the code you are writing. Seems a little redundant, right? But it comes in real handy when your code starts getting huge, especially when you start modifying functions later on down the road. Let’s say you change an old function that has been around forever to return more data. Problem is, you don’t know exactly what code is calling the function and how this will affect that code. That is where unittests come in. If you had a unittest written for this old function you’re modifying, you could run your unittest and it would ‘test’ this function in many different ways to see if it still performed correctly. If the unittest fails, it will tell you how and why.
If you take this theory and expand upon it, and you have a decent suite of unittests accumulating, you can run these tests all the time, as your building your complex code base. Then whenever you make a small change that breaks a test, you’ll know it immediately because your unittest caught it right away and you don’t have to wait until production hits and your code breaks at a critical moment.
Luckily Python supports unittesting with a module called unittest:
“unittest supports test automation, sharing of setup and shutdown code for tests, aggregation of tests into collections, and independence of the tests from the reporting framework.”
Here’s a super-simple sample rigging function I wish I had a unittest for:
# fileName: rigging.py from pymel.core.nodetypes import Joint def createRoot(): rootJoint = Joint(n='root') return rootJoint
I make a unittest file:
import unittest from rigging import createRoot class TestCreateRoot(unittest.TestCase): def test_createRoot(self): myRoot = createRoot() self.assertEquals(myRoot, 'root') self.assertTrue(isinstance(myRoot, Joint)) suiteCreateRoot = unittest.TestLoader().loadTestsFromTestCase(TestCreateRoot) unittest.TextTestRunner(verbosity=2).run(suiteCreateRoot)
You’ll notice the “asserts” being used. Asserts are a simple way to claim that something is equal (first and second arg being equal in this case) or returns True and erroring otherwise. They seem to be the primary way to test things in unittests. In this case, I’m testing the name of the joint on line 08 and the type of the object as type ‘Joint’ on line 09.
The trick to getting unittests for all your code is to code them while you are coding your actual code. In fact, you are supposed to actually write the unittest before your actual code! Huh! That doesn’t make sense. But it does if you think about it. Your unittest will force you to consider exactly what you want your code to do and what it will return. In a way, it makes you pre-plan your code a bit. That’s the theory anyway. Long story short, try to write your unittest as you write your regular code. This takes longer at the beginning but will save you tons of time in the long run. It is truly one of the last steps towards making you a real top-notch, pro-level coder. 🙂
More for you to explore:
1. The coolest way to run these unittests is to setup a commandLine mayapy.exe session to run them for you really fast in the background. Check-out WingIDE for awesome unittesting features
2. Create ‘suites’ of tests
3. Use setUp() and tearDown() to get your scene ready for your tests
That should get you started! Let the questions begin . . .
(I’ll try to answer as I find time)
Very informative! I’ve, just as you described, heard about unittests but never really understood what they were for. I can definately see the use for them now.
Thanks for a great explanation.
In case you want to create more than one TestCase subclass in your file and having the runner pick all of their tests up you can:
import sys
suiteCreateRoot = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__])