Metadata-Version: 1.0
Name: NoseGAE
Version: 0.1.1
Summary: NoseGAE: nose plugin for Google App Engine testing
Home-page: http://code.google.com/p/nose-gae/
Author: Jason Pellerin
Author-email: jpellerin@gmail.com
License: LGPL License
Description: <?xml version="1.0" encoding="utf-8" ?>
        <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
        <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
        <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
        <meta name="generator" content="Docutils 0.4: http://docutils.sourceforge.net/" />
        <title>NoseGAE: Test support for Google Application Engine</title>
        <style type="text/css">
        
        /*
        :Author: David Goodger
        :Contact: goodger@users.sourceforge.net
        :Date: $Date: 2005-12-18 01:56:14 +0100 (Sun, 18 Dec 2005) $
        :Revision: $Revision: 4224 $
        :Copyright: This stylesheet has been placed in the public domain.
        
        Default cascading style sheet for the HTML output of Docutils.
        
        See http://docutils.sf.net/docs/howto/html-stylesheets.html for how to
        customize this style sheet.
        */
        
        body {
        font-family: verdana, arial, sans-serif;
        font-size: small;
        padding-right: 5em;
        }
        
        /* used to remove borders from tables and images */
        .borderless, table.borderless td, table.borderless th {
        border: 0 }
        
        table.borderless td, table.borderless th {
        /* Override padding for "table.docutils td" with "! important".
        The right padding separates the table cells. */
        padding: 0 0.5em 0 0 ! important }
        
        .first {
        /* Override more specific margin styles with "! important". */
        margin-top: 0 ! important }
        
        .last, .with-subtitle {
        margin-bottom: 0 ! important }
        
        .hidden {
        display: none }
        
        a.toc-backref {
        text-decoration: none ;
        color: black }
        
        blockquote.epigraph {
        margin: 2em 5em ; }
        
        dl.docutils dd {
        margin-bottom: 0.5em }
        
        dl.docutils dt {
        font-weight: bold }
        
        div.abstract {
        margin: 2em 5em }
        
        div.abstract p.topic-title {
        font-weight: bold ;
        text-align: center }
        
        div.admonition, div.attention, div.caution, div.danger, div.error,
        div.hint, div.important, div.note, div.tip, div.warning {
        margin: 2em ;
        border: medium outset ;
        padding: 1em }
        
        div.admonition p.admonition-title, div.hint p.admonition-title,
        div.important p.admonition-title, div.note p.admonition-title,
        div.tip p.admonition-title {
        font-weight: bold ;
        font-family: sans-serif }
        
        div.attention p.admonition-title, div.caution p.admonition-title,
        div.danger p.admonition-title, div.error p.admonition-title,
        div.warning p.admonition-title {
        color: red ;
        font-weight: bold ;
        font-family: sans-serif }
        
        div.compound .compound-first, div.compound .compound-middle {
        margin-bottom: 0.5em }
        
        div.compound .compound-last, div.compound .compound-middle {
        margin-top: 0.5em }
        
        div.dedication {
        margin: 2em 5em ;
        text-align: center ;
        font-style: italic }
        
        div.dedication p.topic-title {
        font-weight: bold ;
        font-style: normal }
        
        div.figure {
        margin-left: 2em ;
        margin-right: 2em }
        
        div.footer, div.header {
        clear: both;
        font-size: smaller }
        
        div.line-block {
        display: block ;
        margin-top: 1em ;
        margin-bottom: 1em }
        
        div.line-block div.line-block {
        margin-top: 0 ;
        margin-bottom: 0 ;
        margin-left: 1.5em }
        
        div.sidebar {
        margin-left: 1em ;
        border: medium outset ;
        padding: 1em ;
        background-color: #ffffee ;
        width: 40% ;
        float: right ;
        clear: right }
        
        div.sidebar p.rubric {
        font-family: sans-serif ;
        font-size: medium }
        
        div.system-messages {
        margin: 5em }
        
        div.system-messages h1 {
        color: red }
        
        div.system-message {
        border: medium outset ;
        padding: 1em }
        
        div.system-message p.system-message-title {
        color: red ;
        font-weight: bold }
        
        div.topic {
        margin: 2em }
        
        h1.section-subtitle, h2.section-subtitle, h3.section-subtitle,
        h4.section-subtitle, h5.section-subtitle, h6.section-subtitle {
        margin-top: 0.4em }
        
        h1.title {
        text-align: center }
        
        h2.subtitle {
        text-align: center }
        
        hr.docutils {
        width: 75% }
        
        img.align-left {
        clear: left }
        
        img.align-right {
        clear: right }
        
        ol.simple, ul.simple {
        margin-bottom: 1em }
        
        ol.arabic {
        list-style: decimal }
        
        ol.loweralpha {
        list-style: lower-alpha }
        
        ol.upperalpha {
        list-style: upper-alpha }
        
        ol.lowerroman {
        list-style: lower-roman }
        
        ol.upperroman {
        list-style: upper-roman }
        
        p.attribution {
        text-align: right ;
        margin-left: 50% }
        
        p.caption {
        font-style: italic ;
        color: #aaa;
        margin-top: -6px;
        padding-top: 0px;
        margin-left: 2em;
        font-size: smaller;
        }
        
        p.credits {
        font-style: italic ;
        font-size: smaller }
        
        p.label {
        white-space: nowrap }
        
        p.rubric {
        font-weight: bold ;
        font-size: larger ;
        color: maroon ;
        text-align: center }
        
        p.sidebar-title {
        font-family: sans-serif ;
        font-weight: bold ;
        font-size: larger }
        
        p.sidebar-subtitle {
        font-family: sans-serif ;
        font-weight: bold }
        
        p.topic-title {
        font-weight: bold }
        
        pre.address {
        margin-bottom: 0 ;
        margin-top: 0 ;
        font-family: serif ;
        font-size: 100% }
        
        pre.literal-block, pre.doctest-block {
        margin-left: 2em ;
        margin-right: 2em ;
        background-color: #eeeeee }
        
        span.classifier {
        font-family: sans-serif ;
        font-style: oblique }
        
        span.classifier-delimiter {
        font-family: sans-serif ;
        font-weight: bold }
        
        span.interpreted {
        font-family: sans-serif }
        
        span.option {
        white-space: nowrap }
        
        span.pre {
        white-space: pre }
        
        span.problematic {
        color: red }
        
        span.section-subtitle {
        /* font-size relative to parent (h1..h6 element) */
        font-size: 80% }
        
        table.citation {
        border-left: solid 1px gray;
        margin-left: 1px }
        
        table.docinfo {
        margin: 2em 4em }
        
        table.docutils {
        margin-top: 0.5em ;
        margin-bottom: 0.5em }
        
        table.footnote {
        border-left: solid 1px black;
        margin-left: 1px }
        
        table.docutils td, table.docutils th,
        table.docinfo td, table.docinfo th {
        padding-left: 0.5em ;
        padding-right: 0.5em ;
        vertical-align: top }
        
        table.docutils th.field-name, table.docinfo th.docinfo-name {
        font-weight: bold ;
        text-align: left ;
        white-space: nowrap ;
        padding-left: 0 }
        
        h1 tt.docutils, h2 tt.docutils, h3 tt.docutils,
        h4 tt.docutils, h5 tt.docutils, h6 tt.docutils {
        font-size: 100% }
        
        tt.docutils {
        background-color: #eeeeee }
        
        ul.auto-toc {
        list-style-type: none }
        
        pre.response.pass, .pass
        {
        background: #dfd; !important
        }
        
        pre.response.fail, .fail
        {
        background: #faa; !important
        }
        
        li.fail
        {
        background: #fff;
        }
        
        li.fail a
        {
        color: #f55;
        font-weight: bold;
        }
        
        tt.request.method
        {
        font-weight: bold;
        }
        
        .contents
        {
        float: right;
        font-size: smaller;
        border: 1px solid #ddd;
        padding-left: 1em;
        padding-right: 1em;
        }
        
        .contents ul
        {
        padding-left: 1em;
        margin-right: .5em;
        margin-left: 0;
        }
        
        .contents p.topic-title
        {
        padding-top: 1ex;
        }
        
        </style>
        </head>
        <body>
        <div class="document" id="nosegae-test-support-for-google-application-engine">
        <h1 class="title">NoseGAE: Test support for Google Application Engine</h1>
        <div class="contents topic">
        <p class="topic-title first"><a id="contents" name="contents">Contents</a></p>
        <ul class="simple">
        <li><a class="reference" href="#overview" id="id1" name="id1">Overview</a></li>
        <li><a class="reference" href="#what-does-it-do" id="id2" name="id2">What does it do?</a><ul>
        <li><a class="reference" href="#functional-tests" id="id3" name="id3">Functional tests</a></li>
        <li><a class="reference" href="#unit-tests" id="id4" name="id4">Unit tests</a></li>
        </ul>
        </li>
        <li><a class="reference" href="#realism-in-testing" id="id5" name="id5">Realism in testing</a></li>
        </ul>
        </div>
        <div class="section">
        <h1><a class="toc-backref" href="#id1" id="overview" name="overview">Overview</a></h1>
        <p>NoseGAE is a <a class="reference" href="http://somethingaboutorange.com/mrl/projects/nose/">nose</a> plugin that makes it easier to write functional and unit
        tests for <a class="reference" href="http://code.google.com/appengine/">Google App Engine</a> applications.</p>
        <p>When the plugin is installed, you can activate it by using the
        <tt class="docutils literal"><span class="pre">--with-gae</span></tt> command line option. The plugin also includes an option
        for setting the path to the Google App Engine python library, if it is
        not in the standard location of <tt class="docutils literal"><span class="pre">/usr/local/google_appengine</span></tt>.</p>
        </div>
        <div class="section">
        <h1><a class="toc-backref" href="#id2" id="what-does-it-do" name="what-does-it-do">What does it do?</a></h1>
        <div class="section">
        <h2><a class="toc-backref" href="#id3" id="functional-tests" name="functional-tests">Functional tests</a></h2>
        <p>The plugin sets up the GAE development environment before your test
        run. This means that you can easily write functional tests for your
        application without having to actually start the dev server and test
        over http. <strong>Note</strong> however that this kind of testing requires that
        your application be a <a class="reference" href="http://www.wsgi.org/wsgi">wsgi</a> application.</p>
        <p>Consider a simple hello world wsgi application:</p>
        <pre class="literal-block">
        import wsgiref.handlers
        from google.appengine.ext import webapp
        
        class Hello(webapp.RequestHandler):
        def get(self):
        self.response.headers['Content-Type'] = 'text/plain'
        self.response.out.write('Hello world!')
        
        def application():
        return webapp.WSGIApplication([('/', Hello)], debug=True)
        
        def main():
        wsgiref.handlers.CGIHandler().run(application())
        
        if __name__ == '__main__':
        main()
        
        </pre>
        <p>And a simple functional test suite for the application:</p>
        <pre class="literal-block">
        from webtest import TestApp
        from helloworld import application
        
        app = TestApp(application())
        
        def test_index():
        response = app.get('/')
        assert 'Hello world!' in str(response)
        
        </pre>
        <p>The important part to note is the <cite>application()</cite> function that
        returns the application. That function provides a way get the
        application under test and call it directly, without having to pass
        through the dev app server. And that's all you need to do for basic
        functional testing.</p>
        <pre class="literal-block">
        <tt class="request method docutils literal"><span class="pre">nosetests</span> <span class="pre">-v</span> <span class="pre">--with-gae</span></tt>
        </pre>
        <pre class="response pass literal-block">
        test.test_index ... ok
        &lt;BLANKLINE&gt;
        ----------------------------------------------------------------------
        Ran 1 test in ...s
        &lt;BLANKLINE&gt;
        OK
        </pre>
        <!--  -->
        </div>
        <div class="section">
        <h2><a class="toc-backref" href="#id4" id="unit-tests" name="unit-tests">Unit tests</a></h2>
        <p>Functional tests are only one kind of test, of course. What if you
        want to write unit tests for your data models? Normally, you can't use
        your models at all outside of the dev environment, because the Google
        App Engine datastore isn't available. However, since the NoseGAE
        plugin sets up the development environment around your test run, you
        can use models directly in your tests.</p>
        <p>Consider a simple models file that includes some doctests:</p>
        <pre class="literal-block">
        from google.appengine.ext import db
        
        class Pet(db.Model):
        &quot;&quot;&quot;
        The Pet class provides storage for pets. You can create a pet:
        
        &gt;&gt;&gt; muffy = Pet(name=u'muffy', type=u'dog', breed=u&quot;Shi'Tzu&quot;)
        &gt;&gt;&gt; muffy # doctest: +ELLIPSIS
        Pet(name=u'muffy', type=u'dog', breed=u&quot;Shi'Tzu&quot;, ...)
        &gt;&gt;&gt; muffy_key = muffy.put()
        
        Once created, you can load a pet by its key:
        
        &gt;&gt;&gt; Pet.get(muffy_key) # doctest: +ELLIPSIS
        Pet(name=u'muffy', type=u'dog', breed=u&quot;Shi'Tzu&quot;, ...)
        
        Or by a query that selects the pet:
        
        &gt;&gt;&gt; list(Pet.all().filter('type = ', 'dog')) # doctest: +ELLIPSIS
        [Pet(name=u'muffy', ...)]
        
        To modify a pet, change one of its properties and ``put()`` it again.
        
        &gt;&gt;&gt; muffy_2 = _[0]
        &gt;&gt;&gt; muffy_2.age = 10
        &gt;&gt;&gt; muffy_key_2 = muffy_2.put()
        
        The pet's key doesn't change when it is updated.
        
        &gt;&gt;&gt; bool(muffy_key == muffy_key_2)
        True
        &quot;&quot;&quot;
        name = db.StringProperty(required=True)
        type = db.StringProperty(required=True,
        choices=set([&quot;cat&quot;, &quot;dog&quot;, &quot;bird&quot;,
        &quot;fish&quot;, &quot;monkey&quot;]))
        breed = db.StringProperty()
        age = db.IntegerProperty()
        comments = db.TextProperty()
        created = db.DateTimeProperty(auto_now_add=True, required=True)
        
        def __repr__(self):
        return (&quot;Pet(name=%r, type=%r, breed=%r, age=%r, &quot;
        &quot;comments=%r, created=%r)&quot; %
        (self.name, self.type, self.breed, self.age,
        self.comments, self.created))
        
        </pre>
        <p>Without NoseGAE, the doctests fail.</p>
        <pre class="literal-block">
        <tt class="request method docutils literal"><span class="pre">nosetests</span> <span class="pre">-v</span> <span class="pre">--with-doctest</span></tt>
        </pre>
        <pre class="response pass literal-block">
        Failure: ImportError (No module named google.appengine.ext) ... ERROR
        &lt;BLANKLINE&gt;
        ======================================================================
        ERROR: Failure: ImportError (No module named google.appengine.ext)
        ----------------------------------------------------------------------
        Traceback (most recent call last):
        ...
        ImportError: No module named google.appengine.ext
        &lt;BLANKLINE&gt;
        ----------------------------------------------------------------------
        Ran 1 test in ...s
        &lt;BLANKLINE&gt;
        FAILED (errors=1)
        </pre>
        <!--  -->
        <p>With NoseGAE, they pass.</p>
        <pre class="literal-block">
        <tt class="request method docutils literal"><span class="pre">nosetests</span> <span class="pre">-v</span> <span class="pre">--with-doctest</span> <span class="pre">--with-gae</span></tt>
        </pre>
        <pre class="response pass literal-block">
        Doctest: models.Pet ... ok
        &lt;BLANKLINE&gt;
        ----------------------------------------------------------------------
        Ran 1 test in ...s
        &lt;BLANKLINE&gt;
        OK
        </pre>
        <!--  -->
        </div>
        </div>
        <div class="section">
        <h1><a class="toc-backref" href="#id5" id="realism-in-testing" name="realism-in-testing">Realism in testing</a></h1>
        <p>Besides the dev appserver and the datastore, the main sticking point
        for testing Google App Engine applications is the highly restrictive
        runtime environment. When you test without NoseGAE, tests that should
        fail (because the tested code <strong>will fail</strong> when run inside the Google
        App Engine) may pass.</p>
        <p>For instance, consider an app that uses the <cite>socket</cite> module, like this one:</p>
        <pre class="literal-block">
        import socket
        import wsgiref.handlers
        
        class App:
        def __call__(self, environ, start_response):
        # This won't work under GAE, since this is app code
        here = socket.gethostbyname('localhost')
        start_response('200 OK', [('Content-type', 'text/plain')])
        return ['Hello %s' % here]
        
        def application():
        return App()
        
        def main():
        wsgiref.handlers.CGIHandler().run(application())
        
        if __name__ == '__main__':
        main()
        
        </pre>
        <p>With a simple functional test:</p>
        <pre class="literal-block">
        from webtest import TestApp
        from bad_app import application
        import socket
        
        app = TestApp(application())
        
        def test_index_calls_gethostbyname():
        # this works, because in test code GAE sandbox is not active
        host = socket.gethostbyname('localhost')
        response = app.get('/')
        assert 'Hello' in str(response)
        assert host in str(response)
        
        </pre>
        <p>This test will pass when run outside of the Google App Engine
        environment.</p>
        <pre class="literal-block">
        <tt class="request method docutils literal"><span class="pre">nosetests</span> <span class="pre">-v</span></tt>
        </pre>
        <pre class="response pass literal-block">
        test.test_index_calls_gethostbyname ... ok
        &lt;BLANKLINE&gt;
        ----------------------------------------------------------------------
        Ran 1 test in ...s
        &lt;BLANKLINE&gt;
        OK
        </pre>
        <!--  -->
        <p>When run with NoseGAE, it will fail, as it should.</p>
        <pre class="literal-block">
        <tt class="request method docutils literal"><span class="pre">nosetests</span> <span class="pre">-v</span> <span class="pre">--with-gae</span></tt>
        </pre>
        <pre class="response pass literal-block">
        test.test_index_calls_gethostbyname ... ERROR
        &lt;BLANKLINE&gt;
        ======================================================================
        ERROR: test.test_index_calls_gethostbyname
        ----------------------------------------------------------------------
        Traceback (most recent call last):
        ...
        AttributeError: 'module' object has no attribute 'gethostbyname'
        &lt;BLANKLINE&gt;
        ----------------------------------------------------------------------
        Ran 1 test in ...s
        &lt;BLANKLINE&gt;
        FAILED (errors=1)
        </pre>
        <!--  -->
        <p>It is important to note that only <strong>application</strong> code is sandboxed by
        NoseGAE. Test code imports outside of the sandbox, so your test code has full
        access to the system and available python libraries, including the Google App
        Engine datastore and other Google App Engine libraries.</p>
        <p>For this reason, <strong>file access is not restricted</strong> in the same way as it
        is under GAE, because it is impossible to differentiate application code file
        access from test code file access.</p>
        </div>
        </div>
        </body>
        </html>
        
Platform: UNKNOWN
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)
Classifier: Topic :: Software Development :: Testing
