I recently worked on a project where I was essentially building a library of rake tasks. Obviously, I wanted to be a good citizen and unit test them. Here are a few notes on techniques I used.
You’re going to want to create a complete sample project to test against. One tricky thing about creating a library rather than an app is that you need an app to test against. It actually turns out to be really helpful because you can create exactly the application you need to test against and leave anything unrelated out. For example, if your task is going to take a bunch of code and bundle it into a gem, you’ll want to put together a very simple bit of code in the proper structure so you can test against it. Put things in your code that are easy to grep for.
You don’t always need to run your tasks for every test. For example, if you have some file or rule based tasks, use the Task#requirements array to your advantage. If something’s not working as expected, it makes sense to first make sure your rules are choosing the proper files. This goes for any set of task dependencies.
Do something about rake’s output. Rake likes to tell you all about the marvelous things it’s doing. This can make reading your test results really freaking difficult. I found a really sweet module in rake’s own test library called CaptureStdout. It provides a two methods, capture_stdout and capture_stderr. These methods accept a block and silence the output of anything executed within them.
Make yourself some convenience methods. You can execute tasks from within a rake environment using Rake::Task[:task_name].invoke. How many times do you want to type that? If your answer is anything shy of “One million please!”, you probably want to make yourself a helper for that. My recommendation is that you buddy your helper up with capture_stdout in any way you find clever.
Think about any environment conditions that may effect your code. This is especially something to consider if you’re dynamically generating tasks based on some external variable (such as RAILS_ENV). The best way I’ve found to handle this is to create separate test files for each environment that need to be tested specifically. Then, just make sure you set the property before you require your library.
Use your setup method efficiently. Make sure you clean any temporary or generated files to give the next test a clean working environment.
Think about how long tasks will take. If you’re only testing a subset of the functionality of your library, choose to test against the most specific task possible. Otherwise, you will end up waiting a long time for your full stack to run in every test.
Lastly, remember that rake, by default, will remember if a task has been run and keep it from running again in the same execution. This is great for avoiding circular dependencies in a running app, but it makes testing just that much more a pain. Allow me to offer this snippet for your enjoyment:
Rake.application.tasks.each {|t| t.instance_eval{@already_invoked = false}}