WritableTestCase: Example Use
Posted on Sun 18 September 2016 in TDDA
In my PyCon UK talk yesterday I promised to update the and document
the copy of writabletestcase.WritableTestCase
on GitHub.
The version I've put up is not quite as powerful as the example I showed in the talk—that will follow—but has the basic functionality.
I've now added examples to the repository and, below, show how these work.
The library is available with
git clone https://github.com/tdda/tdda.git
WritableTestCase
extends unittest.TestCase
, from the Python's standard
library, in three main ways:
-
It provides methods for testing strings produced in memory or files written to disk against reference results in files. When a test fails, rather than just showing a hard-to-read difference, it writes the actual result to file (if necessary) and then shows the
diff
command needed to compare it—something like this:Compare with "diff /path/to/actual-output /path/to/expected-output"
Obviously, the
diff
command can be replaced with a graphical diff tool, anopen
command or whatever.Although this shouldn't be necessary (see below), you also have the option, after verification, or replacing
diff
withcp
to copy the actual output as the new reference output. -
Secondly, the code supports excluding lines of the output contain nominated strings. This is often handy for excluding things like date stamps, version numbers, copyright notices etc. These often appear in output, and vary, without affecting the semantics.
(The version of the library I showed at PyCon had more powerful variants of this, which I'll add later.)
-
Thirdly, if you verify that the new output is correct, the library supports re-running with the
-w
flag to overwrite the expected ("reference") results with the ones generated by the code.Obviously, if this feature is abused, the value of the tests will be lost, but provided you check the output carefully before re-writing, this is a significant convenience.
The example code is in the examples
subdirectory, called
test_using_writabletestcase.py
. It has two test functions,
one of which generates HTML output as a string, and the other
of which produces some slightly different HTML output as a file.
In each case, the output produced by the function is not identical
to the expected "reference" output (in examples/reference
), but
differs only on lines containing "Copyright" and "Version".
Since these are passed into the test as exclusions, the tests should pass.
Here is the example code:
# -*- coding: utf-8 -*-
"""
test_using_writabletestcase.py: A simple example of how to use
tdda.writabletestcase.WritableTestCase.
Source repository: https://github.com/tdda/tdda
License: MIT
Copyright (c) Stochastic Solutions Limited 2016
"""
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
import os
import tempfile
from tdda import writabletestcase
from tdda.examples.generators import generate_string, generate_file
class TestExample(writabletestcase.WritableTestCase):
def testExampleStringGeneration(self):
"""
This test uses generate_string() from tdda.examples.generators
to generate some HTML as a string.
It is similar to the reference HTML in
tdda/examples/reference/string_result.html except that the
Copyright and version lines are slightly different.
As shipped, the test should pass, because the ignore_patterns
tell it to ignore those lines.
Make a change to the generation code in the generate_string
function in generators.py to change the HTML output.
The test should then fail and suggest a diff command to run
to see the difference.
Rerun with
python test_using_writabletestcase.py -w
and it should re-write the reference output to match your
modified results.
"""
actual = generate_string()
this_dir = os.path.abspath(os.path.dirname(__file__))
expected_file = os.path.join(this_dir, 'reference',
'string_result.html')
self.check_string_against_file(actual, expected_file,
ignore_patterns=['Copyright',
'Version'])
def testExampleFileGeneration(self):
"""
This test uses generate_file() from tdda.examples.generators
to generate some HTML as a file.
It is similar to the reference HTML in
tdda/examples/reference/file_result.html except that the
Copyright and version lines are slightly different.
As shipped, the test should pass, because the ignore_patterns
tell it to ignore those lines.
Make a change to the generation code in the generate_file function
in generators.py to change the HTML output.
The test should then fail and suggest a diff command to run
to see the difference.
Rerun with
python test_using_writabletestcase.py -w
and it should re-write the reference output to match your
modified results.
"""
outdir = tempfile.gettempdir()
outpath = os.path.join(outdir, 'file_result.html')
generate_file(outpath)
this_dir = os.path.abspath(os.path.dirname(__file__))
expected_file = os.path.join(this_dir, 'reference',
'file_result.html')
self.check_file(outpath, expected_file,
ignore_patterns=['Copyright', 'Version'])
if __name__ == '__main__':
writabletestcase.main(argv=writabletestcase.set_write_from_argv())
If you download it, and try running it, you should output similar to the following:
$ python test_using_writabletestcase.py
..
----------------------------------------------------------------------
Ran 2 tests in 0.004s
OK
The reference output files it compares against are:
examples/reference/string_result.html
examples/reference/file_result.html
To see what happens when there's a difference, try editing one or both
of the main functions that generate the HTML in generators.py
.
They're most just using explicit strings, so the simplest thing is just
to change a word or something in the output.
If I change It's
to It is
in the generate_string()
function and rerun,
I get this output:
$ python test_using_writabletestcase.py
.
File check failed.
Compare with "diff /var/folders/w7/lhtph66x7h33t9pns0616qk00000gn/T/string_result.html /Users/njr/python/tdda/examples/reference/string_result.html".
Note exclusions:
Copyright
Version
F
======================================================================
FAIL: testExampleStringGeneration (__main__.TestExample)
----------------------------------------------------------------------
Traceback (most recent call last):
File "test_using_writabletestcase.py", line 55, in testExampleStringGeneration
'Version'])
File "/Users/njr/python/tdda/writabletestcase.py", line 294, in check_string_against_file
self.assertEqual(failures, 0)
AssertionError: 1 != 0
----------------------------------------------------------------------
Ran 2 tests in 0.005s
FAILED (failures=1)
1 godel:$
If I then run the diff command it suggests, the output is:
$ diff /var/folders/w7/lhtph66x7h33t9pns0616qk00000gn/T/string_result.html /Users/njr/python/tdda/examples/reference/string_result.html
5,6c5,6
< Copyright (c) Stochastic Solutions, 2016
< Version 1.0.0
—
> Copyright (c) Stochastic Solutions Limited, 2016
> Version 0.0.0
29c29
< It is not terribly exciting.
—
> It's not terribly exciting.
Here you can see the differences that are excluded, and the change I actually made.
(The version I showed at PyCon has an option to see the only the non-excluded differences, but this version doesn't; that will come!)
If I now run again using -w
, to re-write the reference output,
it shows:
$ python test_using_writabletestcase.py -w
.Expected file /Users/njr/python/tdda/examples/reference/string_result.html written.
.
----------------------------------------------------------------------
Ran 2 tests in 0.003s
OK
And, of course, if I run a third time, without -w
, the test now passes:
$ python test_using_writabletestcase.py
..
----------------------------------------------------------------------
Ran 2 tests in 0.003s
OK
So that's a quick overview of it works.