SlideShare ist ein Scribd-Unternehmen logo
1 von 30
Testing My Patience
 Automated Testing for Python Projects

              Adam Lowry
      Portland Python Users Group
               2009-08-11
• Unit Testing
• Integration/Functional Testing
• Web Testing
Maurice Koop: http://www.flickr.com/photos/mauricekoop/1491529630/



               Avoid This
michelle_p: http://www.flickr.com/photos/catalan/3656255672/




     Unit Testing
unittest
import unittest


class TestMath(unittest.TestCase):

    def test_addition(self):
        """Simple addition must be correct."""

        self.assertEquals(2 + 2, 4)
        assert 3 + 3 == 6


if __name__ == '__main__':
    unittest.main()
Running

adam@rye:~/projects/pdxpython$ python test-examples.py --verbose
Simple addition must be correct. ... ok

----------------------------------------------------------------------
Ran 1 test in 0.000s

OK
nose
                        for sanity’s sake
adam@rye:~/projects/pdxpython$ nosetests -v
Simple addition must be correct. ... ok

----------------------------------------------------------------------
Ran 1 test in 0.010 seconds
OK




                     (?:^|[b_.-])[Tt]est
py.test
                          worth considering
(pdxpython)adam@rye:~/projects/pdxpython$ py.test -v test-examples.py
============================= test session starts ==============================
python: platform darwin -- Python 2.5.2 -- /Users/adam/projects/pdxpython/bin/
python2.5 -- pytest-1.0.0
test object 1: /Users/adam/projects/pdxpython/test-examples.py

test-examples.py:6: TestMath.test_addition PASS

=========================== 1 passed in 0.03 seconds ===========================
import nose


def test_add():
    assert 2 + 2 == 3


def test_add2():
    nose.tools.assert_equals(2 + 2, 3)

(pdxpython)adam@rye:~/projects/pdxpython$ nosetests -v nose-examples.py
nose-examples.test_add ... FAIL
nose-examples.test_add2 ... FAIL

======================================================================
FAIL: nose-examples.test_add
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/Users/adam/projects/pdxpython/lib/python2.5/site-packages/nose-0.11.1-py2.5.egg/nose/case.py",
line 183, in runTest
    self.test(*self.arg)
  File "/Users/adam/projects/pdxpython/nose-examples.py", line 5, in test_add
    assert 2 + 2 == 3
AssertionError

======================================================================
FAIL: nose-examples.test_add2
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/Users/adam/projects/pdxpython/lib/python2.5/site-packages/nose-0.11.1-py2.5.egg/nose/case.py",
line 183, in runTest
    self.test(*self.arg)
  File "/Users/adam/projects/pdxpython/nose-examples.py", line 9, in test_add2
    nose.tools.assert_equals(2 + 2, 3)
AssertionError: 4 != 3

----------------------------------------------------------------------
Ran 2 tests in 0.005 seconds
FAILED (failures=2)
class TestMath(unittest.TestCase):

    def setUp(self):
        self.target = 4

    def test_addition(self):
        """Simple addition must be correct."""

        self.assertEquals(2 + 2, self.target)
        assert 3 + 3 == 6




target = None

def set_target():
    global target
    target = 4


@nose.with_setup(set_target)
def test_add():
    nose.tools.assert_equals(2 + 2, target)
Doctest
class Outer(object):
    """Outside worker

     >>> outer = Outer()
     >>> outer.outer_work()
     True

     """
     def __init__(self):
         self.inner = Inner()

     def outer_work(self):
         try:
              return self.inner.inner_work()
         except Exception:
              return False

(pdxpython)adam@rye:~/projects/pdxpython$ nosetests -v --with-doctest worker.py
Doctest: worker.Outer ... ok

----------------------------------------------------------------------
Ran 1 test in 0.006 seconds
OK
Mocks and Stubs
       http://martinfowler.com/articles/mocksArentStubs.html




                     Sakurako Kitsa                                         Andrei Z
http://www.flickr.com/photos/kitsa_sakurako/1117063708/   http://www.flickr.com/photos/andreiz/330632238/
Mock:
“action→assertion”

       Mox:
 “record→replay”
class Inner(object):

    def inner_work(self):
        return True


class Outer(object):

    def __init__(self):
        self.inner = Inner()

    def outer_work(self):
        try:
             return self.inner.inner_work()
        except Exception:
             return False




        Goal: test Outer isolated from Inner
mock.patch()
@mock.patch('worker.Inner.inner_work', lambda x: True)
def test_outer_w_mock():
    outer = worker.Outer()
    nose.tools.assert_equal(outer.outer_work(), True)

@mock.patch('worker.Inner.inner_work', lambda x: False)
def test_outer_inner_fails_w_mock():
    outer = worker.Outer()
    nose.tools.assert_equal(outer.outer_work(), False)
mox.StubOutWithMock()
def test_outer_w_mox():
    moxer = mox.Mox()
    moxer.StubOutWithMock(worker.Inner, 'inner_work')

   worker.Inner.inner_work().AndReturn(True)

   moxer.ReplayAll()
   outer = worker.Outer()
   nose.tools.assert_equal(outer.outer_work(), True)
   moxer.VerifyAll()

   moxer.UnsetStubs()
class TestOuterWMox(mox.MoxTestBase):
    def test_outer_fails(self):
        self.mox.StubOutWithMock(worker.Inner, 'inner_work')

       worker.Inner.inner_work().AndReturn(False)

       self.mox.ReplayAll()
       outer = worker.Outer()
       nose.tools.assert_equal(outer.outer_work(), False)
       self.mox.VerifyAll()
Testing Exceptions
def raiser(obj):
    raise Exception("Oops!")

@mock.patch('worker.Inner.inner_work', raiser)
def test_outer_exc_w_mock():
    outer = worker.Outer()
    nose.tools.assert_equal(outer.outer_work(), False)



   def test_outer_exception(self):
       self.mox.StubOutWithMock(worker.Inner, 'inner_work')

       worker.Inner.inner_work().AndRaise(Exception)

       self.mox.ReplayAll()
       outer = worker.Outer()
       nose.tools.assert_equal(outer.outer_work(), False)
       self.mox.VerifyAll()
This is your application




  AriCee: http://www.flickr.com/photos/aricee/64358127/
class User(object):
    """User object, connected to the user_table

   >>> user = User(password='password')
   >>> db.session.flush()
   >>> user # doctest: +ELLIPSIS
   <User ...>
   >>> user.is_anonymous()
   False
   >>> User.query.filter_by(id=user.id).one() is user
   True
   >>> user.id
   1

   """

    def __init__(self, **kwargs):
Solution: Custom Nose
         Plugin
from nose.plugins import Plugin


class RollbackPlugin(Plugin):
    """Rollback transaction after each test"""

   name = 'rollback'

    def beforeTest(self, test):
        from myapp import test_utils
        test_utils.set_up_app()

    def afterTest(self, test):
        from myapp import db
        db.session.rollback()
        db.session.close()
setup.py
try:
    import ez_setup
    ez_setup.use_setuptools()
except ImportError:
    pass

from setuptools import setup

setup(
    name='RollbackPlugin',
    description='Nose plugin to rollback transaction after each test',
    py_modules=['rollback'],
    version='0.1',
    zip_safe=False,
    entry_points={
        'nose.plugins': [
            'rollback = rollback:RollbackPlugin'
            ]
        }
    )


       nosetests -v --with-doctest --with-rollback
Integration/Functional
        Testing




  fotoopa: http://www.flickr.com/photos/fotoopa_hs/3103153894/
# in tests/__init__.py

server_proc = None

def setup_package():
    global server_proc

    subprocess.call("dropdb myapp-test")
    subprocess.call("createdb myapp-test")

    server_proc = subprocess.Popen("myapp/manage.py runserver --noreload")


def teardown_package():
    server_proc.kill()
Web Testing
Twill
go $home
code 200
find "Don't have an account yet?" # login screen
fv 1 username $username
fv 1 password $password
submit
code 200
notfind 'Sorry'

from twill.commands import *

go(home)
code(200)
find("Don't have an account yet?") # login screen
fv(1, "username", username)
fv(1, "password", password)
submit()
code(200)
notfind('Sorry')
Twill Plugins
def enter_password(email, password):
    """Enters email and password in login form."""
    import twill.commands as cmds
    cmds.fv('email', 'email', email)
    cmds.fv('email', 'password', password)
    cmds.fv('email', 'has_password', 'yes')
    cmds.submit()
    cmds.code(200)


twill.commands.extend_with('myapp.twill_extensions')
Javascript?



   Windmill
References
http://docs.python.org/library/unittest.html

http://docs.python.org/library/doctest.html

nose: http://www.somethingaboutorange.com/mrl/projects/nose/

py.test: http://codespeak.net/py/dist/test/test.html

Mock: http://www.voidspace.org.uk/python/mock/

Mox: http://code.google.com/p/pymox/

Twill: http://twill.idyll.org/

Windmill: http://www.getwindmill.com/
Thanks!

• http://adam.therobots.org/
• http://twitter.com/robotdam
• http://urbanairship.com
• adam@therobots.org

Weitere ähnliche Inhalte

Was ist angesagt?

DjangoCon US 2011 - Monkeying around at New Relic
DjangoCon US 2011 - Monkeying around at New RelicDjangoCon US 2011 - Monkeying around at New Relic
DjangoCon US 2011 - Monkeying around at New Relic
Graham Dumpleton
 
Beginning PHPUnit
Beginning PHPUnitBeginning PHPUnit
Beginning PHPUnit
Jace Ju
 
Unit testing JavaScript using Mocha and Node
Unit testing JavaScript using Mocha and NodeUnit testing JavaScript using Mocha and Node
Unit testing JavaScript using Mocha and Node
Josh Mock
 
Unit testing in iOS featuring OCUnit, GHUnit & OCMock
Unit testing in iOS featuring OCUnit, GHUnit & OCMockUnit testing in iOS featuring OCUnit, GHUnit & OCMock
Unit testing in iOS featuring OCUnit, GHUnit & OCMock
Robot Media
 

Was ist angesagt? (20)

Spock's New Tricks
Spock's New TricksSpock's New Tricks
Spock's New Tricks
 
Javascript Testing with Jasmine 101
Javascript Testing with Jasmine 101Javascript Testing with Jasmine 101
Javascript Testing with Jasmine 101
 
Managing Mocks
Managing MocksManaging Mocks
Managing Mocks
 
DjangoCon US 2011 - Monkeying around at New Relic
DjangoCon US 2011 - Monkeying around at New RelicDjangoCon US 2011 - Monkeying around at New Relic
DjangoCon US 2011 - Monkeying around at New Relic
 
Containers & Dependency in Ember.js
Containers & Dependency in Ember.jsContainers & Dependency in Ember.js
Containers & Dependency in Ember.js
 
Workshop quality assurance for php projects - ZendCon 2013
Workshop quality assurance for php projects - ZendCon 2013Workshop quality assurance for php projects - ZendCon 2013
Workshop quality assurance for php projects - ZendCon 2013
 
UA testing with Selenium and PHPUnit - PFCongres 2013
UA testing with Selenium and PHPUnit - PFCongres 2013UA testing with Selenium and PHPUnit - PFCongres 2013
UA testing with Selenium and PHPUnit - PFCongres 2013
 
Beginning PHPUnit
Beginning PHPUnitBeginning PHPUnit
Beginning PHPUnit
 
Taking a Test Drive
Taking a Test DriveTaking a Test Drive
Taking a Test Drive
 
Testing Java Code Effectively - BaselOne17
Testing Java Code Effectively - BaselOne17Testing Java Code Effectively - BaselOne17
Testing Java Code Effectively - BaselOne17
 
Django (Web Konferencia 2009)
Django (Web Konferencia 2009)Django (Web Konferencia 2009)
Django (Web Konferencia 2009)
 
Unit testing JavaScript using Mocha and Node
Unit testing JavaScript using Mocha and NodeUnit testing JavaScript using Mocha and Node
Unit testing JavaScript using Mocha and Node
 
To Err Is Human
To Err Is HumanTo Err Is Human
To Err Is Human
 
My java file
My java fileMy java file
My java file
 
Threads, Queues, and More: Async Programming in iOS
Threads, Queues, and More: Async Programming in iOSThreads, Queues, and More: Async Programming in iOS
Threads, Queues, and More: Async Programming in iOS
 
Testing JavaScript Applications
Testing JavaScript ApplicationsTesting JavaScript Applications
Testing JavaScript Applications
 
JEEConf 2017 - Having fun with Javassist
JEEConf 2017 - Having fun with JavassistJEEConf 2017 - Having fun with Javassist
JEEConf 2017 - Having fun with Javassist
 
Unit testing in iOS featuring OCUnit, GHUnit & OCMock
Unit testing in iOS featuring OCUnit, GHUnit & OCMockUnit testing in iOS featuring OCUnit, GHUnit & OCMock
Unit testing in iOS featuring OCUnit, GHUnit & OCMock
 
Internet Technology (Practical Questions Paper) [CBSGS - 75:25 Pattern] {Mast...
Internet Technology (Practical Questions Paper) [CBSGS - 75:25 Pattern] {Mast...Internet Technology (Practical Questions Paper) [CBSGS - 75:25 Pattern] {Mast...
Internet Technology (Practical Questions Paper) [CBSGS - 75:25 Pattern] {Mast...
 
Test
TestTest
Test
 

Ähnlich wie Testing My Patience

Python Unit Test
Python Unit TestPython Unit Test
Python Unit Test
David Xie
 
pytest로 파이썬 코드 테스트하기
pytest로 파이썬 코드 테스트하기pytest로 파이썬 코드 테스트하기
pytest로 파이썬 코드 테스트하기
Yeongseon Choe
 
Flask patterns
Flask patternsFlask patterns
Flask patterns
it-people
 

Ähnlich wie Testing My Patience (20)

Practical Celery
Practical CeleryPractical Celery
Practical Celery
 
How to fake_properly
How to fake_properlyHow to fake_properly
How to fake_properly
 
Django Celery
Django Celery Django Celery
Django Celery
 
Pruebas unitarias con django
Pruebas unitarias con djangoPruebas unitarias con django
Pruebas unitarias con django
 
Unit test
Unit testUnit test
Unit test
 
Python Unit Test
Python Unit TestPython Unit Test
Python Unit Test
 
Python magicmethods
Python magicmethodsPython magicmethods
Python magicmethods
 
Advanced Django
Advanced DjangoAdvanced Django
Advanced Django
 
Intro to Testing in Zope, Plone
Intro to Testing in Zope, PloneIntro to Testing in Zope, Plone
Intro to Testing in Zope, Plone
 
Hacking ansible
Hacking ansibleHacking ansible
Hacking ansible
 
Qualidade levada a sério em Python - Emilio Simoni
Qualidade levada a sério em Python - Emilio SimoniQualidade levada a sério em Python - Emilio Simoni
Qualidade levada a sério em Python - Emilio Simoni
 
using python module: doctest
using python module: doctestusing python module: doctest
using python module: doctest
 
Python testing
Python  testingPython  testing
Python testing
 
An Introduction to Celery
An Introduction to CeleryAn Introduction to Celery
An Introduction to Celery
 
Auto testing!
Auto testing!Auto testing!
Auto testing!
 
pytest로 파이썬 코드 테스트하기
pytest로 파이썬 코드 테스트하기pytest로 파이썬 코드 테스트하기
pytest로 파이썬 코드 테스트하기
 
Unit testing
Unit testingUnit testing
Unit testing
 
Token Testing Slides
Token  Testing SlidesToken  Testing Slides
Token Testing Slides
 
Deixa para depois, Procrastinando com Celery em Python
Deixa para depois, Procrastinando com Celery em PythonDeixa para depois, Procrastinando com Celery em Python
Deixa para depois, Procrastinando com Celery em Python
 
Flask patterns
Flask patternsFlask patterns
Flask patterns
 

Kürzlich hochgeladen

+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
?#DUbAI#??##{{(☎️+971_581248768%)**%*]'#abortion pills for sale in dubai@
 
Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024
Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024
Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024
Victor Rentea
 
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers:  A Deep Dive into Serverless Spatial Data and FMECloud Frontiers:  A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Safe Software
 

Kürzlich hochgeladen (20)

ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemkeProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
 
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
 
Rising Above_ Dubai Floods and the Fortitude of Dubai International Airport.pdf
Rising Above_ Dubai Floods and the Fortitude of Dubai International Airport.pdfRising Above_ Dubai Floods and the Fortitude of Dubai International Airport.pdf
Rising Above_ Dubai Floods and the Fortitude of Dubai International Airport.pdf
 
AWS Community Day CPH - Three problems of Terraform
AWS Community Day CPH - Three problems of TerraformAWS Community Day CPH - Three problems of Terraform
AWS Community Day CPH - Three problems of Terraform
 
Web Form Automation for Bonterra Impact Management (fka Social Solutions Apri...
Web Form Automation for Bonterra Impact Management (fka Social Solutions Apri...Web Form Automation for Bonterra Impact Management (fka Social Solutions Apri...
Web Form Automation for Bonterra Impact Management (fka Social Solutions Apri...
 
MS Copilot expands with MS Graph connectors
MS Copilot expands with MS Graph connectorsMS Copilot expands with MS Graph connectors
MS Copilot expands with MS Graph connectors
 
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, AdobeApidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected Worker
 
Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024
Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024
Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024
 
Apidays New York 2024 - Passkeys: Developing APIs to enable passwordless auth...
Apidays New York 2024 - Passkeys: Developing APIs to enable passwordless auth...Apidays New York 2024 - Passkeys: Developing APIs to enable passwordless auth...
Apidays New York 2024 - Passkeys: Developing APIs to enable passwordless auth...
 
[BuildWithAI] Introduction to Gemini.pdf
[BuildWithAI] Introduction to Gemini.pdf[BuildWithAI] Introduction to Gemini.pdf
[BuildWithAI] Introduction to Gemini.pdf
 
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
 
Apidays New York 2024 - The value of a flexible API Management solution for O...
Apidays New York 2024 - The value of a flexible API Management solution for O...Apidays New York 2024 - The value of a flexible API Management solution for O...
Apidays New York 2024 - The value of a flexible API Management solution for O...
 
Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...
Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...
Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...
 
Spring Boot vs Quarkus the ultimate battle - DevoxxUK
Spring Boot vs Quarkus the ultimate battle - DevoxxUKSpring Boot vs Quarkus the ultimate battle - DevoxxUK
Spring Boot vs Quarkus the ultimate battle - DevoxxUK
 
Ransomware_Q4_2023. The report. [EN].pdf
Ransomware_Q4_2023. The report. [EN].pdfRansomware_Q4_2023. The report. [EN].pdf
Ransomware_Q4_2023. The report. [EN].pdf
 
Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024
 
presentation ICT roal in 21st century education
presentation ICT roal in 21st century educationpresentation ICT roal in 21st century education
presentation ICT roal in 21st century education
 
Apidays New York 2024 - The Good, the Bad and the Governed by David O'Neill, ...
Apidays New York 2024 - The Good, the Bad and the Governed by David O'Neill, ...Apidays New York 2024 - The Good, the Bad and the Governed by David O'Neill, ...
Apidays New York 2024 - The Good, the Bad and the Governed by David O'Neill, ...
 
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers:  A Deep Dive into Serverless Spatial Data and FMECloud Frontiers:  A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
 

Testing My Patience

  • 1. Testing My Patience Automated Testing for Python Projects Adam Lowry Portland Python Users Group 2009-08-11
  • 2. • Unit Testing • Integration/Functional Testing • Web Testing
  • 5. unittest import unittest class TestMath(unittest.TestCase): def test_addition(self): """Simple addition must be correct.""" self.assertEquals(2 + 2, 4) assert 3 + 3 == 6 if __name__ == '__main__': unittest.main()
  • 6. Running adam@rye:~/projects/pdxpython$ python test-examples.py --verbose Simple addition must be correct. ... ok ---------------------------------------------------------------------- Ran 1 test in 0.000s OK
  • 7. nose for sanity’s sake adam@rye:~/projects/pdxpython$ nosetests -v Simple addition must be correct. ... ok ---------------------------------------------------------------------- Ran 1 test in 0.010 seconds OK (?:^|[b_.-])[Tt]est
  • 8. py.test worth considering (pdxpython)adam@rye:~/projects/pdxpython$ py.test -v test-examples.py ============================= test session starts ============================== python: platform darwin -- Python 2.5.2 -- /Users/adam/projects/pdxpython/bin/ python2.5 -- pytest-1.0.0 test object 1: /Users/adam/projects/pdxpython/test-examples.py test-examples.py:6: TestMath.test_addition PASS =========================== 1 passed in 0.03 seconds ===========================
  • 9. import nose def test_add(): assert 2 + 2 == 3 def test_add2(): nose.tools.assert_equals(2 + 2, 3) (pdxpython)adam@rye:~/projects/pdxpython$ nosetests -v nose-examples.py nose-examples.test_add ... FAIL nose-examples.test_add2 ... FAIL ====================================================================== FAIL: nose-examples.test_add ---------------------------------------------------------------------- Traceback (most recent call last): File "/Users/adam/projects/pdxpython/lib/python2.5/site-packages/nose-0.11.1-py2.5.egg/nose/case.py", line 183, in runTest self.test(*self.arg) File "/Users/adam/projects/pdxpython/nose-examples.py", line 5, in test_add assert 2 + 2 == 3 AssertionError ====================================================================== FAIL: nose-examples.test_add2 ---------------------------------------------------------------------- Traceback (most recent call last): File "/Users/adam/projects/pdxpython/lib/python2.5/site-packages/nose-0.11.1-py2.5.egg/nose/case.py", line 183, in runTest self.test(*self.arg) File "/Users/adam/projects/pdxpython/nose-examples.py", line 9, in test_add2 nose.tools.assert_equals(2 + 2, 3) AssertionError: 4 != 3 ---------------------------------------------------------------------- Ran 2 tests in 0.005 seconds FAILED (failures=2)
  • 10. class TestMath(unittest.TestCase): def setUp(self): self.target = 4 def test_addition(self): """Simple addition must be correct.""" self.assertEquals(2 + 2, self.target) assert 3 + 3 == 6 target = None def set_target(): global target target = 4 @nose.with_setup(set_target) def test_add(): nose.tools.assert_equals(2 + 2, target)
  • 11. Doctest class Outer(object): """Outside worker >>> outer = Outer() >>> outer.outer_work() True """ def __init__(self): self.inner = Inner() def outer_work(self): try: return self.inner.inner_work() except Exception: return False (pdxpython)adam@rye:~/projects/pdxpython$ nosetests -v --with-doctest worker.py Doctest: worker.Outer ... ok ---------------------------------------------------------------------- Ran 1 test in 0.006 seconds OK
  • 12. Mocks and Stubs http://martinfowler.com/articles/mocksArentStubs.html Sakurako Kitsa Andrei Z http://www.flickr.com/photos/kitsa_sakurako/1117063708/ http://www.flickr.com/photos/andreiz/330632238/
  • 13. Mock: “action→assertion” Mox: “record→replay”
  • 14. class Inner(object): def inner_work(self): return True class Outer(object): def __init__(self): self.inner = Inner() def outer_work(self): try: return self.inner.inner_work() except Exception: return False Goal: test Outer isolated from Inner
  • 15. mock.patch() @mock.patch('worker.Inner.inner_work', lambda x: True) def test_outer_w_mock(): outer = worker.Outer() nose.tools.assert_equal(outer.outer_work(), True) @mock.patch('worker.Inner.inner_work', lambda x: False) def test_outer_inner_fails_w_mock(): outer = worker.Outer() nose.tools.assert_equal(outer.outer_work(), False)
  • 16. mox.StubOutWithMock() def test_outer_w_mox(): moxer = mox.Mox() moxer.StubOutWithMock(worker.Inner, 'inner_work') worker.Inner.inner_work().AndReturn(True) moxer.ReplayAll() outer = worker.Outer() nose.tools.assert_equal(outer.outer_work(), True) moxer.VerifyAll() moxer.UnsetStubs()
  • 17. class TestOuterWMox(mox.MoxTestBase): def test_outer_fails(self): self.mox.StubOutWithMock(worker.Inner, 'inner_work') worker.Inner.inner_work().AndReturn(False) self.mox.ReplayAll() outer = worker.Outer() nose.tools.assert_equal(outer.outer_work(), False) self.mox.VerifyAll()
  • 18. Testing Exceptions def raiser(obj): raise Exception("Oops!") @mock.patch('worker.Inner.inner_work', raiser) def test_outer_exc_w_mock(): outer = worker.Outer() nose.tools.assert_equal(outer.outer_work(), False) def test_outer_exception(self): self.mox.StubOutWithMock(worker.Inner, 'inner_work') worker.Inner.inner_work().AndRaise(Exception) self.mox.ReplayAll() outer = worker.Outer() nose.tools.assert_equal(outer.outer_work(), False) self.mox.VerifyAll()
  • 19. This is your application AriCee: http://www.flickr.com/photos/aricee/64358127/
  • 20. class User(object): """User object, connected to the user_table >>> user = User(password='password') >>> db.session.flush() >>> user # doctest: +ELLIPSIS <User ...> >>> user.is_anonymous() False >>> User.query.filter_by(id=user.id).one() is user True >>> user.id 1 """ def __init__(self, **kwargs):
  • 21. Solution: Custom Nose Plugin from nose.plugins import Plugin class RollbackPlugin(Plugin): """Rollback transaction after each test""" name = 'rollback' def beforeTest(self, test): from myapp import test_utils test_utils.set_up_app() def afterTest(self, test): from myapp import db db.session.rollback() db.session.close()
  • 22. setup.py try: import ez_setup ez_setup.use_setuptools() except ImportError: pass from setuptools import setup setup( name='RollbackPlugin', description='Nose plugin to rollback transaction after each test', py_modules=['rollback'], version='0.1', zip_safe=False, entry_points={ 'nose.plugins': [ 'rollback = rollback:RollbackPlugin' ] } ) nosetests -v --with-doctest --with-rollback
  • 23. Integration/Functional Testing fotoopa: http://www.flickr.com/photos/fotoopa_hs/3103153894/
  • 24. # in tests/__init__.py server_proc = None def setup_package(): global server_proc subprocess.call("dropdb myapp-test") subprocess.call("createdb myapp-test") server_proc = subprocess.Popen("myapp/manage.py runserver --noreload") def teardown_package(): server_proc.kill()
  • 26. Twill go $home code 200 find "Don't have an account yet?" # login screen fv 1 username $username fv 1 password $password submit code 200 notfind 'Sorry' from twill.commands import * go(home) code(200) find("Don't have an account yet?") # login screen fv(1, "username", username) fv(1, "password", password) submit() code(200) notfind('Sorry')
  • 27. Twill Plugins def enter_password(email, password): """Enters email and password in login form.""" import twill.commands as cmds cmds.fv('email', 'email', email) cmds.fv('email', 'password', password) cmds.fv('email', 'has_password', 'yes') cmds.submit() cmds.code(200) twill.commands.extend_with('myapp.twill_extensions')
  • 28. Javascript? Windmill
  • 29. References http://docs.python.org/library/unittest.html http://docs.python.org/library/doctest.html nose: http://www.somethingaboutorange.com/mrl/projects/nose/ py.test: http://codespeak.net/py/dist/test/test.html Mock: http://www.voidspace.org.uk/python/mock/ Mox: http://code.google.com/p/pymox/ Twill: http://twill.idyll.org/ Windmill: http://www.getwindmill.com/
  • 30. Thanks! • http://adam.therobots.org/ • http://twitter.com/robotdam • http://urbanairship.com • adam@therobots.org