Wow<br><br>* easy to understand<br>* quick to write<br>* fast to run<br><br>Contrary to what I may have indicated in the past, I have actually written products with tests, while the tests were valuable, they were generally:<br>
<br>* challenging to read<br>* hard to write<br>* slow to run<br><br>As a result, I have tended to write fewer tests than I should on projects.<br><br>If this thing works as advertised, I may begin to &quot;sup&quot; at the testing &quot;table&quot; more often.<br>
<br><div class="gmail_quote">On Wed, Jul 2, 2008 at 3:41 PM, Martin Aspeli &lt;<a href="mailto:optilude@gmx.net">optilude@gmx.net</a>&gt; wrote:<br><blockquote class="gmail_quote" style="border-left: 1px solid rgb(204, 204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;">
Hi Maurits,<div class="Ih2E3d"><br>
<br>
<blockquote class="gmail_quote" style="border-left: 1px solid rgb(204, 204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;">
I have not tried any code (also not from Dexterity), only read your<br>
article. &nbsp;Some things are not clear to me.<br>
<br>
- At the end of the first mock example you say: &quot;We must remember to put<br>
 &nbsp;the test case into replay mode, using self.replay().&quot; &nbsp;Why? &nbsp;What does<br>
 &nbsp;that do?<br>
</blockquote>
<br></div>
Before you put the mock into replay mode, it&#39;s in &quot;record&quot; mode. All operations you do on it are essentially expectations. For example:<br>
<br>
&nbsp;mymock = self.mocker.mock()<br>
&nbsp;self.expect(mymock.foo()).result(True)<br>
<br>
Now, if I do<br>
<br>
&nbsp;mymock.foo()<br>
<br>
without putthing the mock in replay mode, it&#39;s basically just recording an expectation that foo will be called. In fact, the above could have been rewritten as:<br>
<br>
&nbsp;mymock = self.mocker.mock()<br>
&nbsp;mymock.foo()<br>
&nbsp;self.mocker.result(True)<br>
<br>
though I prefer the self.expect() chaining syntax as it&#39;s more compact.<br>
<br>
Only when the mock is put in replay mode will it actually exhibit the behaviour and check that it&#39;s being called in the expected way. You do that with<br>
<br>
&nbsp;self.replay()<br>
<br>
This is how most mock libraries work. You start off in record mode, doing operations on the object that &quot;record&quot; expectations about how it should be called. You then put it into &quot;replay&quot; 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&#39;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.<div class="Ih2E3d">
<br>
<br>
<blockquote class="gmail_quote" style="border-left: 1px solid rgb(204, 204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;">
- In the third mock example you say that we &quot;set the expectation that<br>
 &nbsp;lookup_schema() should be called on it once&quot;, stressing the word<br>
 &nbsp;&#39;once&#39;. &nbsp;Which part of the test code does that? &nbsp;From the paragraphs<br>
 &nbsp;after that I *guess* that whenever the code is this:<br>
<br>
 &nbsp; &nbsp;self.expect(...).result(...)<br>
<br>
 &nbsp;it implicitly means this:<br>
<br>
 &nbsp; &nbsp;self.expect(...).result(...).count(1, 1)<br>
</blockquote>
&gt;<br>
<blockquote class="gmail_quote" style="border-left: 1px solid rgb(204, 204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;">
 &nbsp;Is that a correct guess?<br>
</blockquote>
<br></div>
Yes.<div class="Ih2E3d"><br>
<br>
<blockquote class="gmail_quote" style="border-left: 1px solid rgb(204, 204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;">
&nbsp;If so, I think you should mention that<br>
 &nbsp;explicitly, perhaps already in the first mock example. &nbsp;<br>
</blockquote>
<br></div>
In record mode, the assumption is that if you record an action once, you meant for it to happen once. Let&#39;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:<br>

<br>
&nbsp;magic_eight_ball = self.mocker.mock()<br>
&nbsp;self.expect(magic_eight_ball()).result(&quot;No&quot;)<br>
&nbsp;self.expect(magic_eight_ball()).result(&quot;Yes&quot;)<div class="Ih2E3d"><br>
<br>
<blockquote class="gmail_quote" style="border-left: 1px solid rgb(204, 204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;">
 &nbsp;BTW, if that is correct then I would find &quot;count(0, None)&quot; a more<br>
 &nbsp;logical default, so by default no restriction on the number of times<br>
 &nbsp;a mocked method is called.<br>
</blockquote>
<br></div>
Most mock libraries don&#39;t work like this. They fail when a method is called too many times (and will tell you so). I think it&#39;s (marginally) better to be explicit if you don&#39;t care how many times something&#39;s called. It&#39;s likely that if you expected something to be called, and it&#39;s called 0 times, then it&#39;s an error, and if it&#39;s called dozens of times, it&#39;s also an error.<div class="Ih2E3d">
<br>
<br>
<blockquote class="gmail_quote" style="border-left: 1px solid rgb(204, 204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;">
And a question: is there next to mock_utility also something like<br>
mock_tool, so you could you this to mock for example the<br>
portal_catalog? &nbsp;I guess something along these lines might work:<br>
<br>
 &nbsp;from whereever import CatalogTool<br>
 &nbsp;catalog_mock = self.mocker.proxy(CatalogTool())<br>
 &nbsp;context_mock = self.mocker.mock()<br>
 &nbsp;self.expect(context_mock.portal_catalog).result(catalog_mock).count(0,None)<br>
</blockquote>
<br></div>
No. There could be, but bear in mind that mock tests are not running in a PloneTestCase (which is why they&#39;re so quick) - at least not normally (no reason they couldn&#39;t be, but then you&#39;re really doing an integration test). As such, if your code calls getToolByName(), you&#39;d probably do a replace() on this.<br>

<br>
A generic mock_tool() could look like this, though:<br>
<br>
&nbsp;from mocker import ANY<br>
<br>
&nbsp;class MockTestCase(...):<br>
 &nbsp; &nbsp; ...<br>
 &nbsp; &nbsp; getToolByName_mock = None<br>
<br>
 &nbsp; &nbsp; def mock_tool(self, tool_mock, name, expected_context=ANY,<br>
 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;min=1, max=None):<br>
 &nbsp; &nbsp; &nbsp; &nbsp; mock = self.getToolByName_mock<br>
 &nbsp; &nbsp; &nbsp; &nbsp; if mock is None:<br>
 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; self.getToolByName_mock = mock = \<br>
 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; self.mocker.replace(&#39;Products.CMFCore.utils.getToolByName&#39;)<br>
 &nbsp; &nbsp; &nbsp; &nbsp; self.expect(mock(expected_context,<br>
 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; name)).result(tool_mock).count(min, max)<br>
<br>
Untested and possibly refactorable of course.<div class="Ih2E3d"><br>
<br>
Martin<br>
<br>
-- <br>
Author of `Professional Plone Development`, a book for developers who<br>
want to work with Plone. See <a href="http://martinaspeli.net/plone-book" target="_blank">http://martinaspeli.net/plone-book</a><br>
<br>
<br>
_______________________________________________<br></div><div><div></div><div class="Wj3C7c">
Product-Developers mailing list<br>
<a href="mailto:Product-Developers@lists.plone.org" target="_blank">Product-Developers@lists.plone.org</a><br>
<a href="http://lists.plone.org/mailman/listinfo/product-developers" target="_blank">http://lists.plone.org/mailman/listinfo/product-developers</a><br>
</div></div></blockquote></div><br>