Lesson 1

In this lesson: how svnmock is organised; your first svnmock session; basic assertions.

Background

svnmock is designed around the idea of "sessions". A given session specifies exactly which API calls will be executed, what order they'll be called in and what parameters they should called with. While it may seem like a lot of work, having to specify every single API call, it has been our experience that this very precise, exacting model saves time in the long run. The more precise the model, the easier it is to find bugs.

MockSession

The central object in a svnmock-based environment is the MockSession. Why? MockSession objects are the wizards behind the curtain, intercepting all calls to the Subversion Python API and making sure they follow the rules you've set down. Sounds complicated? Well, yes, but not for you; we've done our best to keep the magical, pointy bits as far away from the end user as possible.

Let's get started: normally, the first step would be to create a new MockSession object, but svnmock goes ahead and creates one for us (note that this default session is available in the svnmock.mock.active_session global). To manipulate this default session, we need the svnmock.mock submodule:

>>> import svnmock.mock as mock

With that little bit of code, we've taken total control of Subversion's Python bindings. So far, though, the session is empty; we haven't told it to allow any API calls. If you were to try invoking some part of the Subversion API now, this is what you'd see:

>>> import svnmock.mock as mock
>>> 
>>> from svn import core
>>> core.frobnicate()
Traceback (most recent call last):
  [snip]
svnmock.mock.MockError: the MockSession object is empty
>>>

Taking Control

So, now that we've got our MockSession object, what do we do with it? We give it some marching orders. Let's tell it to allow a call to a made-up function, svn.core.frobnicate() (we'll deal with real Subversion functions in the next lesson):

>>> from svn import core
>>> mock.add_command(core.frobnicate)

That's it! Now, let's try that earlier code snippet again.

>>> core.frobnicate()
>>>

You see, no error. The MockSession followed your instructions and allowed the svn.core.frobnicate() call. Keep in mind, though, you've only allowed a single invocation of the function. If it -- or any other function -- would be invoked now...

>>> core.frobnicate()
Traceback (most recent call last):
  [snip]
svnmock.mock.MockError: too many API functions called \
	(last: <function frobnicate at 0xb7925e9c>)
>>>

Controlling Arguments

So far, we've only dealt with parameter-less function calls. Let's push beyond that by making assertions about the arguments given to our made-up function, svn.core.frobnicate().

Let's say we want to assert that svn.core.frobnicate() is called with two parameters, 5 and 6. svnmock allows us to make assertions about a function's arguments, like so:

>>> from svn.core import frobnicate
>>> mock.add_command(frobnicate, (5, 6))
>>> 

The difference is that we've supplied a second parameter to add_command. This tuple specifies the allowed arguments to the function and their order. We can also assert that a function accepts no parameters by using an empty tuple, like so:

>>> from svn.core import frobnicate
>>> mock.add_command(frobnicate, ())
>>> 

In fact, it turns out that the earlier examples we were using (mock.add_command(core.frobnicate), for example) are just shorthand for saying that we expect the function to have no arguments.

So, what happens if a function is called with the wrong arguments?

>>> from svn.core import frobnicate
>>> mock.add_command(frobnicate, (5, 6))
>>>
>>> frobnicate(6, 7)
Traceback (most recent call last):
  [snip]
svnmock.mock.MockError: expected function arguments \
	(5, 6), got (6, 7)
>>> 

svnmock pitches a fit, that's what. It will raise an exception, telling you exactly which arguments it received and which arguments it was expecting.

Return Values

So, now that you know how to make svnmock control what arguments functions are called with, let's move on to talking about function return values.

It turns out that add_command() accepts a third argument that you can use to specify what value the function returns. This argument is optional, and if you don't specify it, svnmock will "make up" a return value, using a little bit of magic we'll get into in the next lesson. For now, though, let's focus on making our svn.core.frobnicate() function do some more tricks, such as returning a tuple of integers:

>>> from svn.core import frobnicate
>>> mock.add_command(frobnicate, (), (7, 7, 7))
>>> 

That can be read as, "expect a call to frobnicate with no arguments, and return (7, 7, 7)". Note that the return value specified in the call to add_command() does not have to match the value frobnicate would actually return. Even if frobnicate would normally raise an exception if given those arguments, svnmock doesn't care; the return value you specify will be returned.

Now, let's invoke the function and see what happens:

>>> frobnicate()
(7, 7, 7)
>>> 

It returned our tuple, as instructed.

Next...

In our next lesson, we'll talk about more complex assertions, such as making sure functionX is getting called with the return value from methodY. Onward to lesson 2!

svnmock