# -*- coding: utf-8 -*-
=======
CHANGES
=======
Release 0.15.0 (2014-07-01)
---------------------------
* [enhance] oktest.web.WSGITest class supports multipart form data.
## build multipart data
from oktest.web import MultiPart
mp = MultiPart() # or boundary='abcdef'; mp = MutliPart(boundary)
mp.add("name1", "value1") # add string value
with open("logo.png", 'wb') as f: # add file value
mp.add("file1", f.read(), "logo.png", "image/png")
## test with multipart data
from oktest.web import WSGITest
http = WSGITest(wsgi_app)
resp = http.POST('/upload', multipart=mp) # or params=mp
ok (resp).is_response(200)
* [enhance] oktest.web.WSGITest class supports 'Cookie' and 'Set-Cookie'.
from oktest.web import WSGITest
http = WSGITest(wsgi_app)
## request: 'Cookie' header
resp = http.GET('/', cookies='name=value') # or cookies={'name':'value'}
## response: 'Set-Cookie' header
ok (resp).is_response(200).cookie('name', 'value')
ok (resp).is_response(200).cookie('name', re.compile(r'^value$'),
domain='www.example.com',
path='/cgi'
expires='Wed, 01-Jan-2020 12:34:56 GMT',
max_age='1200',
secure=True,
httponly=True)
* [enhance] New assertion methods.
ok (xs).all(lambda x: x is None) # ok when all items in xs are None
ok (xs).any(lambda x: x is None) # ok when there is None in xs
ok (x).between(minval, maxval) # ok when minval <= x <= maxval
ok (xs).length([minlen, maxlen]) # ok when minlen <= len(xs) <= maxlen
ok (dictionary).has_key('key') # ok when dictinary has key
ok (dictionary).has_item('key','val') # ok when dictionary has key an val
* [enhance] New utility function 'options_of()' to get user-defined options.
ex:
import unittest
from oktest import ok, test, options_of
class FooTest(unittest.TestCase):
def setUp(self):
## change setUp() behaviour according to tag
dictionary = options_of(self)
if dictionary.get('tag') == "experimental":
....
@test("example", tag="experimental", num=123)
def _(self):
assert options_of(self) == {'tag': "experimental", 'num': 123}
* [bugfix] oktest.web.WSGITest now works on Python 3.4.
* [bugfix] fix oktest.web.WSGITest class to encode urlpath when multibyte.
Release 0.14.2 (2014-03-20)
---------------------------
* [bugfix] Avoids unicode error on environments that sys.stdout.encoding
is 'ISO-8859-1' or 'ANSI_X3.4-1968'.
Release 0.14.1 (2014-03-11)
---------------------------
* [enhance] WSGITest class supports `params' parameter.
Example::
from oktest.web import WSGITest
http = WSGITest(app)
resp = http.GET('/', params={'x':'1'}) # same as http.GET('/', query={'x':'1'})
resp = http.POST('/', params={'x':'1'}) # same as http.GET('/', form={'x':'1'})
Release 0.14.0 (2014-03-10)
---------------------------
* [enhance] Response object returned by `WSGITest#GET()' or '#POST()' now
supports `body_json' property.
Example::
from oktest.web import WSGITest
http = WSGITest(app)
resp = http.GET('/')
print(resp.body_json)
* [change] `headers` argument of `WSGITest#GET()' (or '#POST()' and so on)
now means HTTP headers, not environ values.
Example::
## version <= 0.13
http.GET('/', headers={'HTTP_COOKIE': 'name=val'})
## version >= 0.14
http.GET('/', headers={'Cookie': 'name=val'})
## or
http.GET('/', environ={'HTTP_COOKIE': 'name=val'})
* [enhance] (Experimental) `oktest.validator.Validator' class is added.
It is convenient to test complex data structure.
Example::
from oktest.validator import Validator as V
ok (resp.body_json) == {
"status": "OK",
"member": {
"name": "Haruhi",
"gender": V('gender', enum=('F', 'M')),
"age": V('age', type=int, between=(15, 18)),
"birthday": V('created_at', pattern=r'^\d\d\d\d-\d\d-\d\d$')
}
}
See users guide for details.
http://www.kuwata-lab.com/oktest/oktest-py_users-guide.html#validator
Release 0.13.0 (2014-01-23)
---------------------------
* [enhance] `ok().is_response()' now supports Requests.
Example::
import requests
resp = requests.get('http://www.example.com/')
ok (resp).is_response(200, 'text/html')
* [enhance] (Experimental) Add 'oktest.web' module to help WSGI app testing.
Example::
## create WSGI application
class App(object):
def __call__(self, environ, start_response):
status = '200 OK'
headers = [('Content-Type', 'application/json')]
body = [b'''{"message":"Hello!"}'''] # bytes, not unicode
start_response(status, headers)
return body
app = App()
## test for app
import unittest
import oktest
from oktest import test, ok, subject
from oktest.web import WSGITest # !!!!!
http = WSGITest(app) # !!!!!
https = WSGITest(app, {'HTTPS': 'on'}) # !!!!!
class AppTest(unittest.TestCase):
with subject('GET /'):
@test("Returns messaging JSON.")
def _(self):
resp = http.GET('/') # or http('GET', '/')
ok (resp).is_response(200).json({"message": "Hello!"})
## or
status, headers, body = http.GET('/') # or http('GET', '/')
ok (status) == '200 OK'
ok (headers) == [('Content-Type', 'application/json')]
ok (body) == [b'''{"message":"Hello!"}''']
if __name__ == '__main__':
oktest.main()
Release 0.12.1 (2014-01-20)
---------------------------
* [bugfix] `ok()' object now doesn't have http response assertions.
You must call `ok().is_response()' at first to assert response object. ::
hasattr(ok(response).is_response(), 'json') # True
hasattr(ok(response), 'json') # True on 0.12.0, False on 0.12.1
* [bugfix] `ok().should' now allows method chain.
ex::
ok ("image001.jpg").should.startswith('image').endswith(('.jpg', '.png'))
Release 0.12.0 (2014-01-12)
---------------------------
* [enhance] `ok (actual) == expected' now prints difference between
actual and expected values with `pprint.pformat()' when values are
one of list, tuple, or dict.
ex::
class ExampleTest(unittest.TestCase):
def test_ex1(self):
expected = { 'username': "Haruhi", 'gender': "Female",
'email': "haruhi@sos-brigade.org", }
actual = { 'username': "Haruhi", 'gender': "female",
'email': "haruhi@sos-brigade.org", }
ok (actual) == expected
#
if __name__ == "__main__":
oktest.main()
Above test script reports such as::
AssertionError:
--- expected
+++ actual
@@ -1,3 +1,3 @@
{'email': 'haruhi@sos-brigade.org',
- 'gender': 'Female',
+ 'gender': 'female',
'username': 'Haruhi'}
* [enhance] @at_end decorator registers callback which is invoked after test.
This is similar to tearDown() method, but more flexible than it.
ex::
from oktest import at_end
class HomhomTest(unittest.TestCase):
def test1(self):
## create dummy file
tmpfile = 'dummy.txt'
with open(tmpfile, 'w') as f:
f.write("blablabla")
## register callback to delete dummy file at end of test case
@at_end
def _():
os.unlink(tmpfile)
## do test
with open(tmpfile) as f:
ok (f.read()) == "blablabla"
You can use @at_end instead of fixture release functions.
ex::
class HomhomTest(unittest.TestCase):
##
## fixture provider -- use @at_end instead of releaser
##
def provide_tmpfile(self):
## create dummy file
tmpfile = 'dummy.txt'
with open(tmpfile, 'w') as f:
f.write('blablabla')
## register callback to delete dummy file at end of test case
@at_end
def _():
os.unlink(tmpfile)
## return fixture data
return tmpfile
##
## fixture releaser -- no need to define because we have @at_end!
##
#def release_tmpfile(self, tmpfile):
# os.unlink(tmpfile)
@at_end decorator is similar to unittest.TestCase#atCleanup(),
but the former is called *before* tearDown() and the latter is called
*after* tearDown().
* [enhance] (experimental) New assertion ``ok(response).is_response()``
to assert WebOb or Werkzeug resonse object.
ex::
ok (response).is_response(200) # status code
ok (response).is_response((302, 303)) # status code
ok (response).is_response('200 OK') # status line
ok (response).is_response(200, 'image/jpeg') # content-type
ok (response).is_response(200, re.compile(r'^image/(jpeg|png|gif)$'))
ok (response).is_response(302).header("Location", "/") # header
ok (response).is_response(200).json({"status": "OK"}) # json data
ok (response).is_response(200).body("
Hello
") # response body
ok (response).is_response(200).body(re.compile(".*?
"))
* [enhance] ``ok(x).is_truthy()`` tests whether ``bool(x) == True``.
This is similar to ``ok(bool(x)) == True`` but reports ``x`` value
instead of True/False when assertion failed.
* [enhance] ``ok(x).is_falsy()`` tests whether ``bool(x) == False``.
This is similar to ``ok(bool(x)) == False`` but reports ``x`` value
instead of True/False when assertion failed.
* [enhance] 'oktest.util.from_here()' is defined which adds current
path into sys.path temporarily.
This is useful very much when you want to import a certain module
from current directory or a specific directory.
ex::
from oktest.util import from_here
with from_here():
import mymodule1 # import from directory path of this file
with from_here('../lib'):
import mymodule2 # import from ../lib
* [enhance] 'oktest.util.randstr(n)' is defined which returns random
number string. Width of returned string is n (default 8).
This is very useful when creating fixture data.
ex::
>>> from oktest.util import randstr
>>> randstr(4)
'7327'
>>> randstr(4)
'1598'
>>> randstr(4)
'0362'
>>> randstr()
'38127841'
* [enhance] subject() and situation() now accept user-defined tags.
ex::
class FooTest(unitest.TestCase):
with subject('#method1()', category='basic'):
with situation('when argument is None', category='advanced'):
@test('returns True')
def _(self):
ok (Foo().method1('')) == True
* [enhance] @test decorator regards '[!xxx]' in description as spec id.
ex::
## python
class HomhomTest(unittest.TestCase):
@test('[!bk201] 1+1 should be 2') # spec id is 'bk201'
def _(self):
ok (1+1) == 2
@test('[!nov11] 1-1 should be 0') # spec id is 'nov11'
def _(self):
ok (1-1) == 0
You can filter tests by spec id (sid).
## command-line
bash$ python -m oktest test/example_test.py -f sid=bk201
* [enhance] multiple errors are reported.
For example, if you got error on test method and tearDown(),
both errors are reported.
* [change] '[passed]', '[Failed]', and '[skipped]' are renamed to
'[pass]', '[Fail]', '[skip]' respectively.
Previous output::
- [passed] 1+1 should be 2.
- [Failed] 1-1 should be 0.
## total:1, passed:0, failed:0, error:0, skipped:0, todo:1 (0.000 sec)
New version output::
- [pass] 1+1 should be 2.
- [Fail] 1-1 should be 0.
## total:1, pass:0, fail:0, error:0, skip:0, todo:1 (0.000 sec)
* [bugfix] @test decorator now supports unicode description.
ex::
@test(u"日本語") # error on previous version but not in this release
def _(self):
ok (1+1) == 2
* [bugfix] @todo decorator now supports fixture arguments.
ex::
class FooTest(unittest.TestCase):
#
def provide_x(self):
return 123
#
@test('example')
@todo # error on previous version but not on this release
def _(self, x):
assert False
Release 0.11.1
--------------
* [bugfix] fix '-s verbose' (verbose mode) option to clear long test description.
* [bugfix] fix test reporter not to raise UnicodeEncodeError/UnicodeDecodeError.
Release 0.11.0
--------------
* [change] 'spec()' is now NOT obsoleted.
* [change] 'spec()' is now available as function decorator.
ex::
class FooTest(unittest.TestCase):
def test_method1(self)
@spec("1+1 should be 2")
def _():
ok (1+1) == 2
@spec("1-1 should be 0")
def _():
ok (1-1) == 0
* [enhance] New assertions: not_file(), not_dir() and not_exist().
ex::
ok (".").not_file() # same as NG (".").is_file()
ok (__file__).not_dir() # same as NG (__file__).is_dir()
ok ("foobar").not_exist() # same as NG ("foobar").exists()
* [enhance] New assertion: not_match().
ex::
ok ("SOS").not_match(r"\d+") # same as NG ("SOS").matches(r"\d+")
* [enhance] Global provider/releaser functions can take 'self' argument.
ex::
def provide_logname(self):
self._LOGNAME = os.getenv('LOGNAME')
os.environ['LOGNAME'] = "Haruhi"
return os.environ['LOGNAME']
def release_logname(self, value):
os.environ['LOGNAME'] = self._LOGNAME
* [change] Change not to ignore test classes which name starts with '_'.
* [change] (internal) Move some utility functions to 'util' module.
* [change] (internal) Move '_Context' and '_RunnableContext' classes into 'util' module.
* [change] (internal) Move 'Color' class into 'util' module
* [change] (internal) Remove 'OUT' variable in 'Reporter' class
* [change] (internal) Move 'TARGET_PATTERN' variable to 'config'
* [bugfix] Fix to clear ImportError after trying to import unittest2
Release 0.10.0
--------------
* [change] 'oktest.spec()' is obsoleted completely.
It will print warning message if you use it.
* [change] 'oktest.helper' module is renamed to 'oktest.util'.
('oktest.helper' is still available for backward compabibility.)
* [enhance] Add 'oktest.main()' which is a replacement of 'oktest.run()'.
Using 'oktest.main()' instead of 'oktest.run()', command options are available.
ex::
## for example:
$ python test/foobar_test.py -sp -f test='*keyword*'
## is almost same as:
$ python -m oktest test/foobar_test.py -sp -f test='*keyword*'
* [enhance] Add 'oktest.fail(message)' which is same as 'unittest.fail(message)'.
ex::
from oktest import fail
fail("not impelmented yet") # will raise AssertionError
* [enhance] (Experimental) Add '@todo' decorator which is equivarent to
'@unittest.expectedFailure'.
ex::
from oktest import ok, test, todo
def add(x, y):
return 0 # not implemented yet!
class AddTest(unittest.TestCase):
@test("returns sum of arguments.")
@todo # equivarent to @unittest.expectedFailure
def _(self):
ok (10, 20) == 30 ## will be failed expectedly
## (because not implemented yet)
Expected failure of assertion is reported as '[TODO]', not '[Failed]'.
* [enhance] (Experimental) Test context supported.
It helps you to describe specification in structured style.
ex::
from oktest import ok, test
from oktest import subject, situation
class SampleTestCase(unittest.TestCase):
SUBJECT = "class 'Sample'"
with subject("method1()"):
with situation("when condition:"):
@test("spec1")
def _(self):
...
@test("spec2")
def _(self):
...
with situation("else:"):
@test("spec3")
def _(self):
...
if __name__ == '__main__':
import oktest
oktest.main()
Output exmple::
$ python test/example_test.py
* class 'Sample'
+ method1()
+ when condition:
- [passed] spec1
- [passed] spec2
+ else:
- [passed] spec3
## total:3, passed:3, failed:0, error:0, skipped:0, todo:0 (0.000 sec)
* [change] Output is changed.
###
### previous version
###
$ python test/foo_test.py
* FooTest
- [ok] test1
- [ok] test2
- [skipped] test3
## total:3, passed:2, failed:0, error:0, skipped:1 (elapsed 0.000)
###
### in this release
###
$ python test/foo_test.py
* FooTest
- [passed] test1
- [passed] test2
- [skipped] test3 (reason: REASON)
## total:3, passed:2, failed:0, error:0, skipped:1, todo:0 (0.000 sec)
Release 0.9.0
-------------
* New '@test' decorator provided. It is simple but very powerful.
Using @test decorator, you can write test description in free text
instead of test method.
ex::
class FooTest(unittest.TestCase):
def test_1_plus_1_should_be_2(self): # not cool...
self.assertEqual(2, 1+1)
@test("1 + 1 should be 2") # cool! easy to read & write!
def _(self):
self.assertEqual(2, 1+1)
* Fixture injection support by '@test' decorator.
Arguments of test method are regarded as fixture names and
they are injected by @test decorator automatically.
Instance methods or global functions which name is 'provide_xxxx' are
regarded as fixture provider (or builder) for fixture 'xxxx'.
ex::
class SosTest(unittest.TestCase):
##
## fixture providers.
##
def provide_member1(self):
return {"name": "Haruhi"}
def provide_member2(self):
return {"name": "Kyon"}
##
## fixture releaser (optional)
##
def release_member1(self, value):
assert value == {"name": "Haruhi"}
##
## testcase which requires 'member1' and 'member2' fixtures.
##
@test("validate member's names")
def _(self, member1, member2):
assert member1["name"] == "Haruhi"
assert member2["name"] == "Kyon"
Dependencies between fixtures are resolved automatically.
ex::
class BarTest(unittest.TestCase):
##
## for example:
## - Fixture 'a' depends on 'b' and 'c'.
## - Fixture 'c' depends on 'd'.
##
def provide_a(b, c): return b + c + ["A"]
def provide_b(): return ["B"]
def provide_c(d): return d + ["C"]
def provide_d(): reutrn ["D"]
##
## Dependencies between fixtures are solved automatically.
##
@test("dependency test")
def _(self, a):
assert a == ["B", "D", "C", "A"]
If loop exists in dependency then @test reports error.
If you want to integrate with other fixture library, see the following
example::
class MyFixtureManager(object):
def __init__(self):
self.values = { "x": 100, "y": 200 }
def provide(self, name):
return self.values[name]
def release(self, name, value):
pass
oktest.fixure_manager = MyFixtureResolver()
* Supports command-line interface to execute test scripts.
ex::
## run test scripts except foo_*.py in plain style
$ python -m oktest -x 'foo_*.py' -sp tests/*_test.py
## run test scripts in 'tests' dir with pattern '*_test.py'
$ python -m oktest -p '*_test.py' tests
## filter by class name
$ python -m oktest -f class='ClassName*' tests
## filter by test method name
$ python -m oktest -f test='*keyword*' tests
## filter by user-defined option added by @test decorator
$ python -m oktest -f tag='*value*' tests
Try ``python -m oktest -h`` for details about command-line options.
* Reporting style is changed. Oktest now provides three reporing styles.
- plain (similar to unittest)
- simple
- verbose (default)
All of these styles are colored to emphasize errors.
If you want change reporting style, specify ``-r`` option in command-line.
* New assertion method ``ok(x).attr(name, value)`` to check attribute.
ex::
d = datetime.date(2000, 12, 31)
ok (d).attr('year', 2000).attr('month', 12).attr('date', 31)
* New assertion method ``ok(x).length(n)``. This is same as
``ok(len(x)) == n``, but it is useful when chaining assertion methods.
ex::
ok (func()).is_a(tuple).length(2)
* New feature``ok().should`` helps you to check boolean method.
ex::
## same as ok ("Haruhi".startswith("Haru")) == True
ok ("Haruhi").should.startswith("Haru")
## same as ok ("Haruhi".isupper()) == False
ok ("Haruhi").should_not.isupper()
* 'ok(str1) == str2' displays diff if text1 != text2, even when using
with unittest module. In previous version, text diff is displayed
only when using oktest.run().
If you are unittest user and using Python < 2.7, use 'ok(str1) == str2'
instead of 'self.assertEqual(str2, str1)' to display text diff.
* Assertion ``raises()`` supports regular expression to check error message.
def fn(): raise ValueError("ERROR-123")
ok (fn).raises(ValueError, re.compile(r'^[A-Z]+-\d+$'))
* Helper functions in oktest.dummy module are now available as decorator.
This is useful when you must use Python 2.4::
from oktest.dummy import dummy_io
## for Python 2.4
@dummy_io("SOS")
def d_io():
assert sys.stdin.read() == "SOS"
print("Haruhi")
sout, serr = d_io
assert sout == "Haruhi\n"
## for Python 2.5 or later
with dummy_io("SOS") as d_io:
assert sys.stdin.read() == "SOS"
print("Haruhi")
sout, serr = d_io
assert sout == "Haruhi\n"
* 'AssertionObject.expected' is renamed to 'AssertionObject.boolean'.
You should update your custom assertion definition.
ex::
import oktest
@oktest.assertion
def startswith(self, arg):
boolean = self.target.startswith(arg)
#if boolean == self.expected: # obsolete
if boolean == self.boolean:
return True
self.failed("%r.startswith(%r) : failed." % (self.target, arg))
* ``oktest.run()`` is changed to return number of failures and errors
of tests.::
sys.exit(oktest.run()) # status code == number of failures and errors
* ``before_each()`` and ``after_each()`` are now non-supported.
Use ``before()`` and ``after()`` intead.
* (Experimental) New function ``NOT()`` provided which is same as ``NG()``.
* (Experimental) ``skip()`` and ``@skip.when()`` are provided to skip tests::
from oktest import skip
class FooTest(unittest.TestCase):
def test_1(self):
if sys.version.startswith('2.'):
reason = "not available on Python 2.x"
skip(reason) # raises SkipTest
...
@skip.when(sys.version.startswith('2.'), "not available on Python 2.x")
def test_2(self):
...
If you want to use @skip.when with @test decorator, see the folloing::
## OK
@test("description")
@skip.when(condition, "reason")
def _(self):
...
## NG
@skip.when(condition, "reason")
@test("description")
def _(self):
...
Release 0.8.0
-------------
* add ``NG()`` which is same as not_ok().
* enhanced to proive egg files for Python 3.
* enhanced to support assertion method chaining. ::
ok ("sos".upper()).is_a(str).matches(r'^[A-Z]+$') == "SOS"
* ``ok().matches()`` can take flag parameter which is passed to re.compile().
ok ("\nSOS\n").matches(r'^[A-Z]+$', re.M)
## same as:
#ok("\nSOS\n").matches(r.compile(r'^[A-Z]$', re.M))
* enhance helper methods to be available without with-statement.
(this is necessary for Python 2.4 which is default version on CentOS.)
from oktest.helper import chdir
def fn():
ok (os.getcwd()) == "/tmp"
chdir("/tmp").run(fn)
## this is same as:
#with chdir("/tmp"):
# ok (os.getcwd()) == "/tmp"
from oktest.dummy import dummy_file
def fn():
ok ("A.txt").is_file()
ok (open("A.txt").read()) == "SOS"
dummy_file("A.txt", "SOS").run(fun)
## this is same as:
#with dummy_file("A.txt", "SOS"):
# ok (open("A.txt").read()) == "SOS"
* ``spec()`` now checks environment variable $SPEC.
This is useful to filter test cases.
## test script
from oktest import oktest, run
class StrTest(object):
def test_upper(self):
if spec("returns upper case string"):
ok ("sos".upper()) == "SOS"
if spec("doesn't change non-alphabetics"):
ok ("sos123<>".upper()) == "SOS123<>"
if __name__ == "__main__":
run()
## terminal
$ SPEC="returns upper case string" python test1.py
* fix ``oktest.run()`` to print correct traceback if ok() is called from
nested function.
* fix content of README.txt.
Release 0.7.0
-------------
* enhanced to allow users to define custom assertion functions. ::
import oktest
from oktest import ok
#
@oktest.assertion
def startswith(self, arg):
boolean = self.target.startswith(arg)
if boolean == self.expected:
return True
self.failed("%r.startswith(%r) : failed." % (self.target, arg))
#
ok ("Sasaki").startswith("Sas")
* rename 'ok().hasattr()' to 'ok().has_attr()'.
(but old name is also available for backward compatibility.)
* change 'chdir()' to take a function as 2nd argument. ::
def f():
... do_something ...
chdir('build', f)
# The above is same as:
with chdir('build'):
... do_something ...
* add document of 'oktest.helper.dummy_io()'. ::
with dummy_io("SOS") as io:
assert sys.stdin.read() == "SOS"
print("Haruhi")
assert io.stdout == "Haruhi\n"
assert io.stderr == ""
* fix 'oktest.tracer.Call#__repr__()' to change output according to
whether '==' is called or not. This is aimed to make output of
'ok(tr[0]) == [...]' to be more readable.
* change 'Runner#run()' to skip AssertionError if it is raised by
'assert ....' and not 'ok() == ...'.
Release 0.6.0
-------------
* enhanced to add 'oktest.tracer.Tracer' class. see README for details.
* change 'run()' to sort classes order by lineno in source file.
* change default argument of 'run()' from '.*Test(Case)$' to
'.*(Test|TestCase|_TC)$'.
Release 0.5.0
-------------
* change default argument of 'run()' to '.*Test(Case)$'.
* enhanced to report untested AssertionObject.
* new helper function 'spec()' which describes test specification.
* new helper function 'dummy_values()' which changes dictionary temporarily.
* new helper function 'dummy_attrs()' which changes object's attributes temporarily.
* 'TestCaseRunner' class is renamed to 'TestRunner'.
* (undocumented) new helper function 'dummy_environ_vars()'.
* (undocumented) new helper function 'using()'.
* (uncodumented) add rm_rf()
Release 0.4.0
-------------
* enhanced to support 'ok (x).in_delta(y, d)' which raises assertion exception
unless y-d < x < y+d.
* change test script to support Python 2.7 and 3.x.
* fixed several bugs.
Release 0.3.0
-------------
* enhanced 'ok (s1) == s2' to display unified diff (diff -u)
* changed to call 'before()/after()' instead of 'before_each()/after_each()'
(currently 'before_each()/after_each()' is also enabled but they will be
disabled in the future)
* improved compatibility with unittest module
* (internal) 'ValueObject' class is renamed to 'AssertionObject'
* (internal) 'Reporter#before_each()' and '#after_each()' are renamed into
'#before()' and '#after()'
Release 0.2.2
-------------
* enhanced to set 'f.exception' after 'ok (f).raises(Exception)'
to get raised exception object
* changed to flush output after '.'/'f'/'E' printed
* change to get exception message by 'str(ex)' instead of 'ex.message'
Release 0.2.1
-------------
* fix 'REAMDE.txt'
* fix 'setup.py'
Release 0.2.0
-------------
* public release