Merge lp:~leonardr/lazr.restful/make-test-class-public into lp:lazr.restful

Proposed by Leonard Richardson
Status: Merged
Approved by: Edwin Grubbs
Approved revision: 128
Merged at revision: not available
Proposed branch: lp:~leonardr/lazr.restful/make-test-class-public
Merge into: lp:lazr.restful
Prerequisite: lp:~leonardr/lazr.restful/refactor-tag-request-with-version
Diff against target: 377 lines (+147/-135)
2 files modified
src/lazr/restful/testing/webservice.py (+138/-3)
src/lazr/restful/tests/test_webservice.py (+9/-132)
To merge this branch: bzr merge lp:~leonardr/lazr.restful/make-test-class-public
Reviewer Review Type Date Requested Status
Edwin Grubbs (community) code Approve
Review via email: mp+20574@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Leonard Richardson (leonardr) wrote :

lazr.restful's unit test file test_webservice.py used to have a class called WebServiceTestCase that would set up a dummy web service and run some unit tests against it. Over time, the amount of setup work necessary increased, to the point where WSTC was basically setting up a fully functional web service.

When integrating a new lazr.restful version into Launchpad, I noticed that Launchpad also had unit tests that ran against a web service, and that I could save myself a lot of trouble by writing unit tests that subclassed WebServiceTestCase. I did this, but I pledged to come back and move WSTC (and its dependent classes) into lazr.restful.testing.webservice, so that Launchpad wouldn't be importing classes from a lazr.restful unit test module.

This branch does basically that, and nothing else. The one fix I made in passing was to change SimpleWebServiceConfiguration.createRequest to use the utility function tag_request_with_version_name(), replacing code that was basically a copy of tag_request_with_version_name. I also had to change the WADL unit tests, because I fleshed out the doctest strings of IGenericEntry and IGenericConfiguration.

Revision history for this message
Edwin Grubbs (edwin-grubbs) wrote :
Download full text (9.1 KiB)

Hi Leonard,

The changes look good, but I got these errors when running the tests under python2.6. There were even more problems under python2.5.

-Edwin

Running lazr.restful.example.base.tests.test_integration.WSGILayer tests:
  Set up lazr.restful.example.base.tests.test_integration.FunctionalLayer in 0.328 seconds.
  Set up lazr.restful.example.base.tests.test_integration.WSGILayer in 0.000 seconds.

Failure in test /home/egrubbs/canonical/launchpadlib/refactor-tag-request-with-version/src/lazr/restful/example/base/tests/field.txt
Traceback (most recent call last):
  File "/usr/lib/python2.5/unittest.py", line 260, in run
    testMethod()
  File "/usr/lib/python2.5/doctest.py", line 2128, in runTest
    raise self.failureException(self.format_failure(new.getvalue()))
AssertionError: Failed doctest test for field.txt
  File "/home/egrubbs/canonical/launchpadlib/refactor-tag-request-with-version/src/lazr/restful/example/base/tests/field.txt", line 0

----------------------------------------------------------------------
File "/home/egrubbs/canonical/launchpadlib/refactor-tag-request-with-version/src/lazr/restful/example/base/tests/field.txt", line 55, in field.txt
Failed example:
    print new_value
Differences (ndiff with -expected +actual):
    - "http://.../cookbooks/The%20Joy%20of%20Cooking"
    ? ^^^^
    + "http:\/\/cookbooks.dev\/devel\/cookbooks\/The%20Joy%20of%20Cooking"
    ? + ^ +++++++++++++++++++++++
----------------------------------------------------------------------
File "/home/egrubbs/canonical/launchpadlib/refactor-tag-request-with-version/src/lazr/restful/example/base/tests/field.txt", line 58, in field.txt
Failed example:
    print webservice(link_field_url, 'PATCH', new_value)
Differences (ndiff with -expected +actual):
      HTTP/1.1 209 Content Returned
    - ...
    + Status: 209 Content Returned
    + Content-Length: 68
      Content-Type: application/json
    - ...
    + X-Powered-By: Zope (www.zope.org), Python (www.python.org)
      <BLANKLINE>
    - "http://cookbooks.dev/.../cookbooks/The%20Joy%20of%20Cooking"
    ? ^^^
    + "http:\/\/cookbooks.dev\/devel\/cookbooks\/The%20Joy%20of%20Cooking"
    ? + + + ^^^^^^ +

  Ran 9 tests with 1 failures and 0 errors in 2.391 seconds.
Running lazr.restful.example.multiversion.tests.test_integration.WSGILayer tests:
  Tear down lazr.restful.example.base.tests.test_integration.WSGILayer in 0.000 seconds.
  Tear down lazr.restful.example.base.tests.test_integration.FunctionalLayer in 0.000 seconds.
  Set up lazr.restful.example.multiversion.tests.test_integration.FunctionalLayer in 0.078 seconds.
  Set up lazr.restful.example.multiversion.tests.test_integration.WSGILayer in 0.000 seconds.
  Ran 3 tests with 0 failures and 0 errors in 0.406 seconds.
Running lazr.restful.example.wsgi.tests.test_integration.WSGILayer tests:
  Tear down lazr.restful.example.multiversion.tests.test_integration.WSGILayer in 0.001 seconds.
  Tear down lazr.restful.example.multiversion.tests.test_integration.FunctionalLayer in 0.000 seconds.
  Set up lazr.restful.example.wsgi.tests.test_integration.FunctionalLayer in 0.069 se...

Read more...

review: Needs Fixing
Revision history for this message
Leonard Richardson (leonardr) wrote :

I think you're running the tests on an old version of simplejson. Can you run some code to see what version of simplejson you have? Here's what I get:

$ bin/py
>>> import simplejson
>>> simplejson.__version__
'2.0.9'

Also try making this change to setup.py, rerun bin/buildout, and see if the problem goes away.

=== modified file 'setup.py'
--- setup.py 2009-10-07 18:54:39 +0000
+++ setup.py 2010-03-04 14:03:26 +0000
@@ -64,7 +64,7 @@
         'lazr.uri',
         'martian==0.11',
         'setuptools',
- 'simplejson',
+ 'simplejson>=2.0.9',
         'van.testing',
         'wsgiref',
         'zope.app.pagetemplate',

Revision history for this message
Edwin Grubbs (edwin-grubbs) wrote :

Hi Leonard,

Updating simplejson fixed it.

merge-approved

-Edwin

review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'src/lazr/restful/testing/webservice.py'
--- src/lazr/restful/testing/webservice.py 2010-03-03 13:05:13 +0000
+++ src/lazr/restful/testing/webservice.py 2010-03-03 17:07:18 +0000
@@ -9,7 +9,11 @@
9 'ExampleWebServicePublication',9 'ExampleWebServicePublication',
10 'FakeRequest',10 'FakeRequest',
11 'FakeResponse',11 'FakeResponse',
12 'IGenericEntry',
13 'IGenericCollection',
12 'pprint_entry',14 'pprint_entry',
15 'WebServiceTestCase',
16 'WebServiceTestConfiguration'
13 'WebServiceTestPublication',17 'WebServiceTestPublication',
14 'WebServiceTestRequest',18 'WebServiceTestRequest',
15 'TestPublication',19 'TestPublication',
@@ -19,25 +23,39 @@
19import os23import os
20import traceback24import traceback
21import simplejson25import simplejson
26import sys
27from types import ModuleType
22import urllib28import urllib
29import unittest
23from urlparse import urljoin30from urlparse import urljoin
24import wsgi_intercept31import wsgi_intercept
2532
26from zope.component import adapts, getUtility, queryMultiAdapter33from zope.component import (
27from zope.interface import implements34 adapts, getGlobalSiteManager, getUtility, queryMultiAdapter)
35from zope.configuration import xmlconfig
36from zope.interface import alsoProvides, implements, Interface
28from zope.publisher.browser import BrowserRequest37from zope.publisher.browser import BrowserRequest
29from zope.publisher.interfaces.http import IHTTPApplicationRequest38from zope.publisher.interfaces.http import IHTTPApplicationRequest
30from zope.publisher.paste import Application39from zope.publisher.paste import Application
31from zope.proxy import ProxyBase40from zope.proxy import ProxyBase
41from zope.schema import TextLine
32from zope.security.checker import ProxyFactory42from zope.security.checker import ProxyFactory
43from zope.testing.cleanup import CleanUp
33from zope.traversing.browser.interfaces import IAbsoluteURL44from zope.traversing.browser.interfaces import IAbsoluteURL
3445
46from lazr.restful.declarations import (
47 collection_default_content, exported,
48 export_as_webservice_collection, export_as_webservice_entry,
49 export_read_operation, operation_parameters)
35from lazr.restful.interfaces import (50from lazr.restful.interfaces import (
36 IServiceRootResource, IWebServiceClientRequest,51 IServiceRootResource, IWebServiceClientRequest,
37 IWebServiceConfiguration, IWebServiceLayer, IWebServiceVersion)52 IWebServiceConfiguration, IWebServiceLayer, IWebServiceVersion)
38from lazr.restful.publisher import (53from lazr.restful.publisher import (
39 WebServicePublicationMixin, WebServiceRequestTraversal)54 WebServicePublicationMixin, WebServiceRequestTraversal)
40from lazr.restful.simple import Publication, Request55from lazr.restful.simple import (
56 BaseWebServiceConfiguration, Publication, Request,
57 RootResourceAbsoluteURL, ServiceRootResource)
58from lazr.restful.utils import tag_request_with_version_name
4159
42from lazr.uri import URI60from lazr.uri import URI
4361
@@ -385,6 +403,121 @@
385 raise ValueError(self.body)403 raise ValueError(self.body)
386404
387405
406class WebServiceTestConfiguration(BaseWebServiceConfiguration):
407 """A simple configuration for tests that need a running web service."""
408 implements(IWebServiceConfiguration)
409 show_tracebacks = False
410 active_versions = ['1.0', '2.0']
411 hostname = "webservice_test"
412 last_version_with_mutator_named_operations = None
413
414 def createRequest(self, body_instream, environ):
415 request = Request(body_instream, environ)
416 request.setPublication(
417 WebServiceTestPublication(getUtility(IServiceRootResource)))
418 tag_request_with_version_name(request, '2.0')
419 return request
420
421
422class IWebServiceTestRequest10(IWebServiceClientRequest):
423 """A marker interface for requests to the '1.0' web service."""
424
425
426class IWebServiceTestRequest20(IWebServiceClientRequest):
427 """A marker interface for requests to the '2.0' web service."""
428
429
430class WebServiceTestCase(CleanUp, unittest.TestCase):
431 """A test case for web service operations."""
432
433 testmodule_objects = []
434
435 def setUp(self):
436 """Set the component registry with the given model."""
437 super(WebServiceTestCase, self).setUp()
438
439 # Register a simple configuration object.
440 webservice_configuration = WebServiceTestConfiguration()
441 sm = getGlobalSiteManager()
442 sm.registerUtility(webservice_configuration)
443
444 # Register IWebServiceVersions for the
445 # '1.0' and '2.0' web service versions.
446 alsoProvides(IWebServiceTestRequest10, IWebServiceVersion)
447 sm.registerUtility(
448 IWebServiceTestRequest10, IWebServiceVersion, name='1.0')
449 alsoProvides(IWebServiceTestRequest20, IWebServiceVersion)
450 sm.registerUtility(
451 IWebServiceTestRequest20, IWebServiceVersion, name='2.0')
452
453 # Register a service root resource
454 service_root = ServiceRootResource()
455 sm.registerUtility(service_root, IServiceRootResource)
456
457 # Register an IAbsoluteURL adapter for the service root resource.
458 sm.registerAdapter(
459 RootResourceAbsoluteURL,
460 [IServiceRootResource, IWebServiceClientRequest], IAbsoluteURL)
461
462 # Build a test module that exposes the given resource interfaces.
463 testmodule = ModuleType('testmodule')
464 for interface in self.testmodule_objects:
465 setattr(testmodule, interface.__name__, interface)
466 sys.modules['lazr.restful.testmodule'] = testmodule
467
468 # Register the test module in the ZCML configuration: adapter
469 # classes will be built automatically.
470 xmlconfig.string("""
471 <configure
472 xmlns="http://namespaces.zope.org/zope"
473 xmlns:webservice="http://namespaces.canonical.com/webservice">
474 <include package="zope.component" file="meta.zcml" />
475 <include package="zope.security" file="meta.zcml"/>
476 <include package="lazr.restful" file="meta.zcml" />
477 <include package="lazr.restful" file="configure.zcml" />
478
479 <adapter for="*"
480 factory="zope.traversing.adapters.DefaultTraversable"
481 provides="zope.traversing.interfaces.ITraversable" />
482
483 <webservice:register module="lazr.restful.testmodule" />
484 </configure>
485 """)
486
487
488class IGenericEntry(Interface):
489 """A simple, reusable entry interface for use in tests.
490
491 The entry publishes one field and one named operation.
492 """
493 export_as_webservice_entry()
494
495 # pylint: disable-msg=E0213
496 a_field = exported(
497 TextLine(
498 title=u'A "field"',
499 description=u'The only field that can be <> 0 in the entry.'))
500
501 @operation_parameters(
502 message=TextLine(title=u'Message to say'))
503 @export_read_operation()
504 def greet(message):
505 """Print an appropriate greeting based on the message.
506
507 :param message: This will be included in the greeting.
508 """
509
510
511class IGenericCollection(Interface):
512 """A simple collection containing `IGenericEntry`, for use in tests."""
513 export_as_webservice_collection(IGenericEntry)
514
515 # pylint: disable-msg=E0211
516 @collection_default_content()
517 def getAll():
518 """Returns all the entries."""
519
520
388class DummyRootResource:521class DummyRootResource:
389 """A root resource that does nothing."""522 """A root resource that does nothing."""
390 implements(IServiceRootResource)523 implements(IServiceRootResource)
@@ -403,3 +536,5 @@
403 def __str__(self):536 def __str__(self):
404 return "http://dummy"537 return "http://dummy"
405 __call__ = __str__538 __call__ = __str__
539
540
406541
=== modified file 'src/lazr/restful/tests/test_webservice.py'
--- src/lazr/restful/tests/test_webservice.py 2010-03-03 13:05:13 +0000
+++ src/lazr/restful/tests/test_webservice.py 2010-03-03 17:07:18 +0000
@@ -6,18 +6,14 @@
66
7from cStringIO import StringIO7from cStringIO import StringIO
8from operator import attrgetter8from operator import attrgetter
9import sys
10from types import ModuleType
11import unittest9import unittest
1210
13from zope.component import getGlobalSiteManager, getUtility11from zope.component import getGlobalSiteManager, getUtility
14from zope.configuration import xmlconfig12from zope.interface import implements, Interface
15from zope.interface import alsoProvides, implements, Interface
16from zope.publisher.browser import TestRequest13from zope.publisher.browser import TestRequest
17from zope.schema import Date, Datetime, TextLine14from zope.schema import Date, Datetime, TextLine
18from zope.security.management import (15from zope.security.management import (
19 endInteraction, newInteraction, queryInteraction)16 endInteraction, newInteraction, queryInteraction)
20from zope.testing.cleanup import CleanUp
21from zope.traversing.browser.interfaces import IAbsoluteURL17from zope.traversing.browser.interfaces import IAbsoluteURL
2218
23from lazr.restful.fields import Reference19from lazr.restful.fields import Reference
@@ -25,15 +21,11 @@
25 ICollection, IEntry, IEntryResource, IResourceGETOperation,21 ICollection, IEntry, IEntryResource, IResourceGETOperation,
26 IServiceRootResource, IWebServiceConfiguration,22 IServiceRootResource, IWebServiceConfiguration,
27 IWebServiceClientRequest, IWebServiceVersion)23 IWebServiceClientRequest, IWebServiceVersion)
28from lazr.restful import (24from lazr.restful import EntryResource, ResourceGETOperation
29 EntryResource, ServiceRootResource, ResourceGETOperation)25from lazr.restful.declarations import exported, export_as_webservice_entry
30from lazr.restful.simple import BaseWebServiceConfiguration, Request
31from lazr.restful.declarations import (
32 collection_default_content, exported, export_as_webservice_collection,
33 export_as_webservice_entry, export_read_operation, operation_parameters)
34from lazr.restful.simple import RootResourceAbsoluteURL
35from lazr.restful.testing.webservice import (26from lazr.restful.testing.webservice import (
36 create_web_service_request, WebServiceTestPublication)27 create_web_service_request, IGenericCollection, IGenericEntry,
28 WebServiceTestCase, WebServiceTestPublication)
37from lazr.restful.testing.tales import test_tales29from lazr.restful.testing.tales import test_tales
38from lazr.restful.utils import (30from lazr.restful.utils import (
39 get_current_browser_request, get_current_web_service_request,31 get_current_browser_request, get_current_web_service_request,
@@ -69,125 +61,10 @@
69 IResourceGETOperation, name=name)61 IResourceGETOperation, name=name)
7062
7163
72class IGenericEntry(Interface):
73 """A simple, reusable entry interface.
74
75 This is the description of the entry.
76 """
77 export_as_webservice_entry()
78
79 # pylint: disable-msg=E0213
80 a_field = exported(
81 TextLine(
82 title=u'A "field"',
83 description=u'The only field that can be <> 0 in the entry.'))
84
85 @operation_parameters(
86 message=TextLine(title=u'Message to say'))
87 @export_read_operation()
88 def greet(message):
89 """Print an appropriate greeting based on the message.
90
91 :param message: This will be included in the greeting.
92 """
93
94
95class IGenericCollection(Interface):
96 """A simple collection containing `IGenericEntry`."""
97 export_as_webservice_collection(IGenericEntry)
98
99 # pylint: disable-msg=E0211
100 @collection_default_content()
101 def getAll():
102 """Returns all the entries."""
103
104
105class IWebServiceRequest10(IWebServiceClientRequest):
106 """A marker interface for requests to the '1.0' web service."""
107
108
109class IWebServiceRequest20(IWebServiceClientRequest):
110 """A marker interface for requests to the '2.0' web service."""
111
112
113class SimpleWebServiceConfiguration(BaseWebServiceConfiguration):
114 implements(IWebServiceConfiguration)
115 show_tracebacks = False
116 active_versions = ['1.0', '2.0']
117 hostname = "webservice_test"
118 last_version_with_mutator_named_operations = None
119
120 def createRequest(self, body_instream, environ):
121 request = Request(body_instream, environ)
122 request.version = '2.0'
123 alsoProvides(request, IWebServiceRequest20)
124 request.setPublication(WebServiceTestPublication(
125 getUtility(IServiceRootResource)))
126 request.annotations[request.VERSION_ANNOTATION] = '2.0'
127 return request
128
129
130class WebServiceTestCase(CleanUp, unittest.TestCase):
131 """A test case for web service operations."""
132
133 testmodule_objects = []
134
135 def setUp(self):
136 """Set the component registry with the given model."""
137 super(WebServiceTestCase, self).setUp()
138
139 # Register a simple configuration object.
140 webservice_configuration = SimpleWebServiceConfiguration()
141 sm = getGlobalSiteManager()
142 sm.registerUtility(webservice_configuration)
143
144 # Register IWebServiceVersions for the
145 # '1.0' and '2.0' web service versions.
146 alsoProvides(IWebServiceRequest10, IWebServiceVersion)
147 sm.registerUtility(
148 IWebServiceRequest10, IWebServiceVersion, name='1.0')
149 alsoProvides(IWebServiceRequest20, IWebServiceVersion)
150 sm.registerUtility(
151 IWebServiceRequest20, IWebServiceVersion, name='2.0')
152
153 # Register a service root resource
154 service_root = ServiceRootResource()
155 sm.registerUtility(service_root, IServiceRootResource)
156
157 # Register an IAbsoluteURL adapter for the service root resource.
158 sm.registerAdapter(
159 RootResourceAbsoluteURL,
160 [IServiceRootResource, IWebServiceClientRequest], IAbsoluteURL)
161
162 # Build a test module that exposes the given resource interfaces.
163 testmodule = ModuleType('testmodule')
164 for interface in self.testmodule_objects:
165 setattr(testmodule, interface.__name__, interface)
166 sys.modules['lazr.restful.testmodule'] = testmodule
167
168 # Register the test module in the ZCML configuration: adapter
169 # classes will be built automatically.
170 xmlconfig.string("""
171 <configure
172 xmlns="http://namespaces.zope.org/zope"
173 xmlns:webservice="http://namespaces.canonical.com/webservice">
174 <include package="zope.component" file="meta.zcml" />
175 <include package="zope.security" file="meta.zcml"/>
176 <include package="lazr.restful" file="meta.zcml" />
177 <include package="lazr.restful" file="configure.zcml" />
178
179 <adapter for="*"
180 factory="zope.traversing.adapters.DefaultTraversable"
181 provides="zope.traversing.interfaces.ITraversable" />
182
183 <webservice:register module="lazr.restful.testmodule" />
184 </configure>
185 """)
186
187
188class IHas_getitem(Interface):64class IHas_getitem(Interface):
189 pass65 pass
19066
67
191class Has_getitem:68class Has_getitem:
192 implements(IHas_getitem)69 implements(IHas_getitem)
193 def __getitem__(self, item):70 def __getitem__(self, item):
@@ -320,8 +197,8 @@
320 'entry/wadl_entry:doc', entry=entry).splitlines()197 'entry/wadl_entry:doc', entry=entry).splitlines()
321 self.assertEquals([198 self.assertEquals([
322 '<wadl:doc xmlns="http://www.w3.org/1999/xhtml">',199 '<wadl:doc xmlns="http://www.w3.org/1999/xhtml">',
323 '<p>A simple, reusable entry interface.</p>',200 '<p>A simple, reusable entry interface for use in tests.</p>',
324 '<p>This is the description of the entry.</p>',201 '<p>The entry publishes one field and one named operation.</p>',
325 '',202 '',
326 '</wadl:doc>'], doclines)203 '</wadl:doc>'], doclines)
327204
@@ -339,7 +216,7 @@
339 ).splitlines()216 ).splitlines()
340 self.assertEquals([217 self.assertEquals([
341 '<wadl:doc xmlns="http://www.w3.org/1999/xhtml">',218 '<wadl:doc xmlns="http://www.w3.org/1999/xhtml">',
342 'A simple collection containing IGenericEntry.',219 'A simple collection containing IGenericEntry, for use in tests.',
343 '</wadl:doc>'], doclines)220 '</wadl:doc>'], doclines)
344221
345 def test_field_wadl_doc (self):222 def test_field_wadl_doc (self):

Subscribers

People subscribed via source and target branches