==================
PicoTest.py README
==================

Release: 0.2.0

.. contents::



Overview
========

PicoTest.py is a small but very useful testing library for Python.



Basic Example
=============

PicoTest.py uses ``with`` statement instead of ``TestCase`` class.

* Test topc or context is represented by ``with`` statement.
* Test spec is represented by ``@test`` decorator.

examples/1_basic_test.py::

    import picotest
    test = picotest.new()

    with test("assertion example"):

        @test("1+1 should be 2")
        def _():                # 'self' is not required
            assert 1+1 == 2

        @test("assertion methods of unittest are avaiable")
        def _(self):
            self.assertEqual("Haruhi".upper(), "HARUHI")

        @test("'assertTextEqual()' is available which shows diff of two texts")
        def _(self):
            expected = "Haruhi\nMikuru\nYuki"
            actual   = "\n".join(["Haruhi", "Mikuru", "Yuki"])
            #actual   = "\n".join(["Haruhi", "Michiru", "Yuki"])
            self.assertTextEqual(expected, actual)

    if __name__ == '__main__':
        picotest.main()


Output example (verbose style)::

    $ python examples/1_basic_test.py
    * assertion example
      - [passed] 1+1 should be 2
      - [passed] assertion methods of unittest are avaiable
      - [passed] 'assertTextEqual()' is available which shows diff of two texts
    ----------------------------------------------------------------------
    ## total:3, passed:3, failed:0, error:0, skipped:0, todo:0


Output example (plain style)::

    $ python examples/1_basic_test.py -sp   # or -s plain
    ...
    ## total:3, passed:3, failed:0, error:0, skipped:0, todo:0



Test Structure
==============

Nested test structure is available.
This makes you to write tests in structured style.

examples/2_structure_test.py::

    import picotest
    test = picotest.new()

    with test("ClassName"):

        with test("#method_name()"):

            with test("when base is not specified..."):

                @test("int('11') should be 11")
                def _():
                    assert int('11') == 11

            with test("when base is specified..."):

                @test("int('11', 16) should be 17")
                def _():
                    assert int('11', 16) == 17

                @test("int('11', 8) should be 9")
                def _():
                    assert int('11', 8) == 9

                @test("int('11', 2) should be 3")
                def _():
                    assert int('11', 2) == 3

    if __name__ == '__main__':
        test.main()



Setup and Teadown Fixture
=========================

Setup and teardown fixtures are available.
They are provided by decorators.

 ``@test.before``
	Same as setUp(). Invoked before each test.

 ``@test.after``
	Same as tearDown(). Invoked after each test.

 ``@test.before_all``
	Similar to setUpClass(). Invoked only once before all tests.

 ``@test.after_all``
	Similar to tearDownClass(). Invoked only once after all tests.

examples/3_setup_teadown_test.py::

    import os
    import picotest
    test = picotest.new()

    with test("fixtures (setup, teardown) example"):
        PWD = os.getcwd()

        @test.before_all   # setupAll
        def _():
            os.mkdir("_sandbox")
            os.chdir("_sandbox")

        @test.after_all    # afterAll
        def _():
            os.chdir(PWD)
            os.rmdir("_sandbox")

        @test.before       # setup
        def _(self):
            self.name = "Haruhi"
            self.team = "SOS"

        @test.after        # teardown
        def _(self):
            pass

        @test("fixture should be called #1")
        def _(self):
            self.assertEqual("Haruhi", self.name)

        @test("fixture should be called #2")
        def _(self):
            self.assertEqual("SOS", self.team)

    if __name__ == '__main__':
        picotest.main()



Fixture Injection
=================

Fixture Injection is available which is more flexible than setup/teardown.

examples/4_fixture_injection_test.py::

    import os
    import picotest
    test = picotest.new()

    with test("fixture injection example"):

        @test.fixture
        def member(self):
            yield "Haruhi"     # use 'yield', not 'return'

        @test.fixture
        def team():            # 'self' is optional
            yield "SOS"

        @test("fixture is injected automatically")
        def _(self, member, team):
            assert member == "Haruhi"
            assert team   == "SOS"

        @test.fixture
        def tmpfile(self):
            ## setup temporary file
            filename = "_tmpfile.txt"
            with open(filename, "w") as f: f.write("SOS\n")
            yield filename
            ## teardown temporary file
            os.unlink(filename)

        @test("temporary file is created and removed automatically")
        def _(tmpfile):
            assert tmpfile == "_tmpfile.txt"
            with open(tmpfile) as f:
                assert f.read() == "SOS\n"

    if __name__ == '__main__':
        picotest.main()



Skip and Todo
=============

PicoTest.py supports test skip and todo.

picotest.skip_when(condition, reason)
    When condition is true then skip rest assertions.

picotest.todo
    Decorator to represents that "feature is not implemented yet".

test.TODO(description):
    Same as::

        @test("...description...")
	@todo
	def _():
	    assert False     # expected failure


example/5_skip_and_todo_test.py::

    import picotest
    from picotest import skip_when, todo
    test = picotest.new()

    with test("skip and todo example"):

        @test("skip test when condition is true")
        def _(self):
            condition = True
            skip_when(condition, "REASON")
            assert 1 == 0     # unreachable

        @test("'@todo' means 'not implemented yet'")
        @todo
        def _(self):
            assert 1 == 0    # expected failure

        test.TODO("something what you have to do #1")
        test.TODO("something what you have to do #2")

    if __name__ == '__main__':
        picotest.main()



Extened Assertion Methods
=========================

PicoTest.py adds some new assertion methods to unittest.TestCase class.

self.assertTextEqual(expected, actual)
	Similar to ``assertEqual()``, but diplays unified diff when actual and
	expected are different text. Equivarent to ``assertMultilineEqual()``
	but available in Python 2.6 or older.
	Example::

	    expected = "Haruhi\nMikuru\nYuki\n"
	    actual   = "Haruhi\nMichiru\nYuki\n"
	    self.assertTextEqual(expected, actual)
	    ## output:
	    # AssertionError: texts are not equal.
	    # --- expected
	    # +++ actual
	    # @@ -1,3 +1,3 @@
	    #  Haruhi
	    # -Mikuru
	    # +Michiru
	    #  Yuki

self.assertException(function, exceptionclass[, errormsg=None])
	Similar to ``assertRaise()``, but this can check error message.
	``errormsg`` can be string or pattern object compiled by ``re.compile()``.
	In addition, you can get exeption object as ``function.exception``.
	Example::

	    def fn(): int("foo")
	    self.assertException(fn, ValueError, "invalid literal for int() with base 10: 'foo'")
	    ## or
	    self.assertException(fn, ValueError, re.compile(r'^invalid literal'));
	    ## or
	    self.assertException(fn, ValueError)
	    self.assertEqual("invalid literal for int() with base 10: 'foo'", str(fn.exception))

self.assertNotException(function[, exceptionclass=Exception])
	Confirms that function doesn't raise exception.
	Example::

	    def fn(): int("FF", 16)
	    self.assertNotException(fn)



Command-line Interface
======================

PicoTest.py provides command-line interface::

    $ python test/foo_test.py        # run test script (verbose style)
    $ python test/foo_test.py -sp    # run test script (plain style)
    $ python test/*.py               # run all test scripts
    $ python -m picotest test/*.py   # run all test scripts
    $ python -m picotest test        # run all under 'test' directory
    $ python -m picotest -h                 # show help
    $ python -m picotest -v                 # print version
    $ python -m picotest -sv test/*.py      # or -s verbose
    $ python -m picotest -ss test/*.py      # or -s simple
    $ python -m picotest -sp test/*.py      # or -s plain
    $ python -m picotest --test='...' test  # filter by description
    $ python -m picotest -D test/*.py       # show all backtrace

``picotest.main()`` exists process with status code which represens number
of failed or error tests::

    $ python examples/1_basic_test.py -sp
    ..f
    ----------------------------------------------------------------------
    [Failed] assertion example > 'assertTextEqual()' is available which shows diff of two texts
       :
    ----------------------------------------------------------------------
    ## total:3, passed:2, failed:1, error:0, skipped:0, todo:0
    $ echo $?
    1                 # number of failed or error tests



Copyright
=========

$Copyright: copyright(c) 2011 kuwata-lab.com all rights reserved $



License
=======

$License: MIT License $
