I'm doing some acceptance tests at work now, as I try to clarify our deployment process. I call them acceptance tests because they are fairly black-box, and are run in a different process.
To do this I added a new fixture to paste.fixture in TestFileEnvironment. It expects to be used in a py.test environment, though that just means it prints a lot (py.test captures prints and only displays them when errors occur) and produces plain assertion errors (without assertEqual style exceptions).
You set it up like:
testenv = TestFileEnvironment( os.path.join(os.path.dirname(__file__), 'scratch')
The testenv object generally writes things in the scratch/ directory. To use it:
def test_paster_create():
# Delete any files in the scratch directory:
testenv.clear()
result = testenv.run('paster', 'create', 'ProjectName')
# Make sure a file or directory was created:
assert 'ProjectName' in result.files_created
# Get a file wrapper:
setup = result.files_created['ProjectName/setup.py']
# Test that the file contains particular text:
setup.mustcontain('ProjectName')
result = testenv.run('rm ProjectName/setup.py')
assert 'ProjectName/setup.py' in result.files_deleted
If an exit code is non-zero, or anything is written to stderr, then an exception is raised. It looks for what files were created, deleted, or updated during the command too. There's a couple other methods, but I haven't needed a whole lot yet.
Anyway, I've found it makes an often annoying process (automatically testing command-line scripts) much more pleasant. And subprocess is like a million times better than all the ways we had to execute commands in the past. OK, a million might be high -- 4x at least (a hint: just use the .communicate() method, the other methods are Too Hard).