Unit testing and SupyBot
Ahhh.. I have a supybot plugin under testing. I am still learning the ins and outs (and probably should read through all the source code to see what’s possible. The testing framework supybot-test is really more of an acceptance test framework, but it runs without you having to set up and run the whole setup. It’s quite nice in its way. It allows me to test-drive changes (which is excellent!).
The framework is based on some basic convention for file naming and directory structure. I didn’t have that structure to begin with, but I’ve added it. Now I can easily test the plugin, though I will have to make some changes to the version control setup later.
Mocking was a little different. I needed to stub out the file reading bit, because I didn’t want to waste time writing and reading files. I just wanted to know how my plugin worked with different data. I asked on the #supybot irc channel, and got pretty prompt help (though I had to explain the idea of mocking, and I think I wasn’t entirely convincing).
The purpose for my mod is to get data from the existing logfile. I wonder if the lookup plugin that exists already isn’t suitable for the purpose, but I wasn’t so much interested in using supybot as interested in writing some code in python, so I didn’t really do “due diligence” to make sure it was necessary. I suspect the lookup plugin is more complete and cooler in several ways.
The mock/stub/whatever itself is pretty unimpressive. The idea is that you can put anything you want in “mockData” and it will be returned when the plugin tries to read the log file.
mockData = \"\"
def mock_filereader(*args):
return mockData[:]
hooking up the mock in the test routine is pretty easy. I used the setUp routine to find the Stewie plugin, and I simply assigned the mock reader where the method was for reading from the logfile. By the way, this is another good reason to extract out utility methods. It would be hard to do this if it weren’t a method I’d already pulled out.
def setUp(self):
PluginTestCase.setUp(self)
stewie = self.irc.getCallback(\"Stewie\")
stewie._get_logfile = mock_filereader
The plugin “Stewie” was originally written to return Stewie quotes from some animated show I don’t watch (Family Guy, I think). I ended up adding different features to it. I may end up yanking those out and using other plugins eventually. I’m thinking that my “custom” feature might be good for things like returning quotes in context from monty python skits (stored in text). Given a text file, it will return the lines surrounding a given quote. The #tfug guys want to use it to look for quotes from other members, I wonder if it isn’t more widely applicable.
Here is what a test looks like.
def testContextHandlesShortFiles(self):
mockData = [
\"line1\n\",
\"line3\n\",
\"line9\n\",
]
self.setMockData(mockData)
result = self.getMessageText(\"context line3\")
self.assertEquals(\"Context: 3 lines\", result)
for expected in mockData:
text = self.getMessageText(\"more\")
self.assertEquals(expected.strip(), text.strip())
You can see that we give the request “context line3″ , and get back the message text “Context: 3 lines”. Then we look at the “more” message to get the text surrounding the “line3″ content (line1 and line9).
This maybe isn’t 100% transparent yet, but I’m looking at ways to make it more clear. I will be refactoring out a few different bits into explanatory functoins, and may rename some other bits. I think that ultimately it will be quite easy to write and edit plugins.
Anyway, it’s working better all the time, and I’m getting a better handle on it.
