Hi Maurits,

I have not tried any code (also not from Dexterity), only read your
article.  Some things are not clear to me.

- At the end of the first mock example you say: "We must remember to put
  the test case into replay mode, using self.replay()."  Why?  What does
  that do?

Before you put the mock into replay mode, it's in "record" mode. All operations you do on it are essentially expectations. For example:

 mymock = self.mocker.mock()
 self.expect(mymock.foo()).result(True)

Now, if I do

 mymock.foo()

without putthing the mock in replay mode, it's basically just recording an expectation that foo will be called. In fact, the above could have been rewritten as:

 mymock = self.mocker.mock()
 mymock.foo()
 self.mocker.result(True)

though I prefer the self.expect() chaining syntax as it's more compact.

Only when the mock is put in replay mode will it actually exhibit the behaviour and check that it's being called in the expected way. You do that with

 self.replay()

This is how most mock libraries work. You start off in record mode, doing operations on the object that "record" expectations about how it should be called. You then put it into "replay" mode and call the code under test (initialised to use the mock, obviously). The mock library then checks that recorded operations actually happen (in the way that they were recorded) and throws assertion errors if they're not. Finally, you do a verify + restore (implicit in the unit test) that ensures no steps were missed and unpatches anything patched for the test.

- In the third mock example you say that we "set the expectation that
  lookup_schema() should be called on it once", stressing the word
  'once'.  Which part of the test code does that?  From the paragraphs
  after that I *guess* that whenever the code is this:

    self.expect(...).result(...)

  it implicitly means this:

    self.expect(...).result(...).count(1, 1)
>
  Is that a correct guess?

Yes.

 If so, I think you should mention that
explicitly, perhaps already in the first mock example.

In record mode, the assumption is that if you record an action once, you meant for it to happen once. Let's say I have a mock and I expect it to be called twice, returning one thing the first time and something else the second time:

 magic_eight_ball = self.mocker.mock()
 self.expect(magic_eight_ball()).result("No")
 self.expect(magic_eight_ball()).result("Yes")

  BTW, if that is correct then I would find "count(0, None)" a more
  logical default, so by default no restriction on the number of times
  a mocked method is called.

Most mock libraries don't work like this. They fail when a method is called too many times (and will tell you so). I think it's (marginally) better to be explicit if you don't care how many times something's called. It's likely that if you expected something to be called, and it's called 0 times, then it's an error, and if it's called dozens of times, it's also an error.

And a question: is there next to mock_utility also something like
mock_tool, so you could you this to mock for example the
portal_catalog?  I guess something along these lines might work:

  from whereever import CatalogTool
  catalog_mock = self.mocker.proxy(CatalogTool())
  context_mock = self.mocker.mock()
  self.expect(context_mock.portal_catalog).result(catalog_mock).count(0,None)

No. There could be, but bear in mind that mock tests are not running in a PloneTestCase (which is why they're so quick) - at least not normally (no reason they couldn't be, but then you're really doing an integration test). As such, if your code calls getToolByName(), you'd probably do a replace() on this.

A generic mock_tool() could look like this, though:

 from mocker import ANY

 class MockTestCase(...):
     ...
     getToolByName_mock = None

     def mock_tool(self, tool_mock, name, expected_context=ANY,
                    min=1, max=None):
         mock = self.getToolByName_mock
         if mock is None:
             self.getToolByName_mock = mock = \
             self.mocker.replace('Products.CMFCore.utils.getToolByName')
         self.expect(mock(expected_context,
                             name)).result(tool_mock).count(min, max)

Untested and possibly refactorable of course.

Martin

--
Author of `Professional Plone Development`, a book for developers who
want to work with Plone. See http://martinaspeli.net/plone-book


_______________________________________________
Product-Developers mailing list
[email protected]
http://lists.plone.org/mailman/listinfo/product-developers

Reply via email to