Merge pull request #51 from miska/master
Updated documentation reflecting the test I did
This commit is contained in:
commit
538668d290
@ -31,5 +31,129 @@ area of interest.
|
|||||||
In directory fixtures there are resulting xml files obtained from the OBS with
|
In directory fixtures there are resulting xml files obtained from the OBS with
|
||||||
osc api calls.
|
osc api calls.
|
||||||
|
|
||||||
Each xml then needs to be overridden in the test so we mock the right file with
|
Writing tests
|
||||||
the proper url.
|
-------------
|
||||||
|
|
||||||
|
There are few nice building stones available to implement test.
|
||||||
|
|
||||||
|
+responses+ dictionary
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
It contains dictionaries for 'GET', 'PUT', 'POST' and 'ALL'. All of them
|
||||||
|
correspond to the method used by program. 'ALL' is just a shortcut that covers
|
||||||
|
all methods and it is called only if no specific handler is found.
|
||||||
|
|
||||||
|
Dictionaries associated with each method contains as a key relative URL to the
|
||||||
|
top of the server and as data one of the following:
|
||||||
|
|
||||||
|
* path to the file in fixtures directory with XML to return
|
||||||
|
* XML itself starting with +<?xml+
|
||||||
|
* function taking three arguments - 'requst', 'uri' and 'headers'
|
||||||
|
* list which is combination of any of the above
|
||||||
|
|
||||||
|
If handling of +responses+ dictionary is enabled, URL is find in dictionary and
|
||||||
|
appropriate action is taken. If there is XML or path to the XML, XML is
|
||||||
|
returned directly. In case of function, function gets called and is expected to
|
||||||
|
return string that will be passed back. If there is array, first item is used
|
||||||
|
as described above and removed from the array.
|
||||||
|
|
||||||
|
+register_pretty+ function
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
This function should be called before you start running any functions you want
|
||||||
|
to test. It registers handler for 'localhost' requests to be
|
||||||
|
+_pretty_callback+. This handler in short does interpretation of +responses+
|
||||||
|
dictionary as described in previous section and if no handler is found, it
|
||||||
|
raises exception providing URL that program tried to access together with
|
||||||
|
method used.
|
||||||
|
|
||||||
|
example
|
||||||
|
~~~~~~~
|
||||||
|
|
||||||
|
First get template for XML using pythons string Template class. XML has some
|
||||||
|
values replaced with +${variable}+ and we will assign those later.
|
||||||
|
|
||||||
|
.Template
|
||||||
|
[source,xml]
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
<?xml version="1.0"?>
|
||||||
|
<request id="123">
|
||||||
|
<action type="submit">
|
||||||
|
<source project="home:Admin" package="puppet" rev="59f0f46262d7b57b9cdc720c06d5e317"/>
|
||||||
|
<target project="openSUSE:Factory" package="puppet"/>
|
||||||
|
</action>
|
||||||
|
<state name="${request}" who="Admin" when="2014-02-17T12:38:52">
|
||||||
|
<comment>Passed staging project "openSUSE:Factory:Staging:test"</comment>
|
||||||
|
</state>
|
||||||
|
<review state="${review}" when="2014-02-17T12:34:10" who="Admin" by_project="${review_project}">
|
||||||
|
<comment>Passed staging project "openSUSE:Factory:Staging:test"</comment>
|
||||||
|
</review>
|
||||||
|
<description>test</description>
|
||||||
|
</request>
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
[source,python]
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
# Getting template in test
|
||||||
|
tmpl = Template(self._get_fixture_content('request_review.xml'))
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
Now prepare some default starting values
|
||||||
|
[source,python]
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
# clear responses dictionary(just in case)
|
||||||
|
self._clear_responses()
|
||||||
|
# set default response
|
||||||
|
self.responses['GET']['/request/123'] = tmpl.substitute(request='new', review='accepted', review_project="openSUSE:Factory")
|
||||||
|
# register everything
|
||||||
|
self._register_pretty()
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
Now the interesting part. Let's define a function, that would interpret 'POST'
|
||||||
|
method, extract data from command and will actually change the response next
|
||||||
|
'GET' method will give. We can change the responses dictionary anytime, so we
|
||||||
|
can use this to keep state once we get a command to do something.
|
||||||
|
|
||||||
|
[source,python]
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
# Create function making transitions between states
|
||||||
|
def review_change(responses, request, uri):
|
||||||
|
if request.querystring.has_key(u'cmd') and request.querystring[u'cmd'] == [u'addreview']:
|
||||||
|
responses['GET']['/request/123'] = tmpl.substitute(request='review', review='new', review_project=request.querystring[u'by_project'][0])
|
||||||
|
if request.querystring.has_key(u'cmd') and request.querystring[u'cmd'] == [u'changereviewstate']:
|
||||||
|
responses['GET']['/request/123'] = tmpl.substitute(request='new', review=request.querystring[u'newstate'][0], review_project=request.querystring[u'by_project'][0])
|
||||||
|
return responses['GET']['/request/123']
|
||||||
|
|
||||||
|
# Register it
|
||||||
|
self.responses['ALL']['/request/123'] = review_change
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
So whenever somebody sends 'addreview' command, XML that 'GET' on request
|
||||||
|
provides will be changed to reflect newly added review. And whenever somebody
|
||||||
|
sends 'changereviewstate', review will be closed with appropriate state.
|
||||||
|
|
||||||
|
So we have a simple testing framework with multiple states that reacts to the
|
||||||
|
API calls from functions we are testing following the behaviour we specified.
|
||||||
|
So tests itself can be simple.
|
||||||
|
|
||||||
|
[source,python]
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
# Add first review, there is none at beginning
|
||||||
|
api.add_review('123', 'openSUSE:Factory:Staging:A')
|
||||||
|
self.assertEqual(httpretty.last_request().method, 'POST')
|
||||||
|
self.assertEqual(httpretty.last_request().querystring[u'cmd'], [u'addreview'])
|
||||||
|
# Try to add it again, should do anything
|
||||||
|
api.add_review('123', 'openSUSE:Factory:Staging:A')
|
||||||
|
self.assertEqual(httpretty.last_request().method, 'GET')
|
||||||
|
# Accept review
|
||||||
|
api.set_review('123', 'openSUSE:Factory:Staging:A')
|
||||||
|
self.assertEqual(httpretty.last_request().method, 'POST')
|
||||||
|
self.assertEqual(httpretty.last_request().querystring[u'cmd'], [u'changereviewstate'])
|
||||||
|
# Try to accept it again should do anything
|
||||||
|
api.set_review('123', 'openSUSE:Factory:Staging:A')
|
||||||
|
self.assertEqual(httpretty.last_request().method, 'GET')
|
||||||
|
# But we should be able to create a new one
|
||||||
|
api.add_review('123', 'openSUSE:Factory:Staging:A')
|
||||||
|
self.assertEqual(httpretty.last_request().method, 'POST')
|
||||||
|
self.assertEqual(httpretty.last_request().querystring[u'cmd'], [u'addreview'])
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
Loading…
x
Reference in New Issue
Block a user