I’ve been trying to get started with unit-testing while working on a little cli program.
My program basically parses the command line arguments and options, and decides which function to call. Each of the functions performs some operation on a database.
So, for instance, I might have a create function:
def create(self, opts, args):
#I've left out the error handling.
strtime = datetime.datetime.now().strftime("%D %H:%M")
vals = (strtime, opts.message, opts.keywords, False)
self.execute("insert into mytable values (?, ?, ?, ?)", vals)
self.commit()
Should my test case call this function, then execute the select sql to check that the row was entered? That sounds reasonable, but also makes the tests more difficult to maintain. Would you rewrite the function to return something and check for the return value?
Thanks
Alex’s answer covers the dependency injection approach. Another is to factor your method. As it stands, it has two phases: construct a SQL statement, and execute the SQL statement. You don’t want to test the second phase: you didn’t write the SQL engine or the database, you can assume they work properly. Phase 1 is your work: constructing a SQL statement. So you can re-organize the code so that you can test just phase 1:
The
create_sqlfunction is phase 1, and now it’s expressed in a way that lets you write tests directly against it: it takes values and returns values, so you can write unit tests that cover its functionality. Thecreatefunction itself is now simpler, and need not be tested so exhaustively: you could have a few tests that show that it really does execute SQL properly, but you don’t have to cover all the edge cases increate.BTW: This video from Pycon (Tests and Testability) might be interesting.