mock_objects_documentation.html.svn-base
来自「PHP 知识管理系统(基于树结构的知识管理系统), 英文原版的PHP源码。」· SVN-BASE 代码 · 共 758 行 · 第 1/3 页
SVN-BASE
758 行
We don't have to be too fussy about this as the check for which session we want is done elsewhere. We only need to check that it was the same one that came from the session pool. </p> <p> <span class="new_code">findSession()</span> is a factory method the simulation of which is described <a href="#stub">above</a>. The point of departure comes with the first <span class="new_code">expectOnce()</span> call. This line states that whenever <span class="new_code">findSession()</span> is invoked on the mock, it will test the incoming arguments. If it receives the single argument of a string "abc" then a test pass is sent to the unit tester, otherwise a fail is generated. This was the part where we checked that the right session was asked for. The argument list follows the same format as the one for setting return values. You can have wildcards and sequences and the order of evaluation is the same. </p> <p> We use the same pattern to set up the mock logger. We tell it that it should have <span class="new_code">message()</span> invoked once only with the argument "Starting session abc". By testing the calling arguments, rather than the logger output, we insulate the test from any display changes in the logger. </p> <p> We start to run our tests when we create the new <span class="new_code">LoggingSessionPool</span> and feed it our preset mock objects. Everything is now under our control. </p> <p> This is still quite a bit of test code, but the code is very strict. If it still seems rather daunting there is a lot less of it than if we tried this without mocks and this particular test, interactions rather than output, is always more work to set up. More often you will be testing more complex situations without needing this level or precision. Also some of this can be refactored into a test case <span class="new_code">setUp()</span> method. </p> <p> Here is the full list of expectations you can set on a mock object in <a href="http://www.lastcraft.com/simple_test.php">SimpleTest</a>... <table><thead> <tr><th>Expectation</th><th>Needs <span class="new_code">tally()</span></th></tr> </thead><tbody><tr> <td><span class="new_code">expect($method, $args)</span></td> <td style="text-align: center">No</td> </tr> <tr> <td><span class="new_code">expectAt($timing, $method, $args)</span></td> <td style="text-align: center">No</td> </tr> <tr> <td><span class="new_code">expectCallCount($method, $count)</span></td> <td style="text-align: center">Yes</td> </tr> <tr> <td><span class="new_code">expectMaximumCallCount($method, $count)</span></td> <td style="text-align: center">No</td> </tr> <tr> <td><span class="new_code">expectMinimumCallCount($method, $count)</span></td> <td style="text-align: center">Yes</td> </tr> <tr> <td><span class="new_code">expectNever($method)</span></td> <td style="text-align: center">No</td> </tr> <tr> <td><span class="new_code">expectOnce($method, $args)</span></td> <td style="text-align: center">Yes</td> </tr> <tr> <td><span class="new_code">expectAtLeastOnce($method, $args)</span></td> <td style="text-align: center">Yes</td> </tr> </tbody></table> Where the parameters are... <dl> <dt class="new_code">$method</dt> <dd>The method name, as a string, to apply the condition to.</dd> <dt class="new_code">$args</dt> <dd> The arguments as a list. Wildcards can be included in the same manner as for <span class="new_code">setReturn()</span>. This argument is optional for <span class="new_code">expectOnce()</span> and <span class="new_code">expectAtLeastOnce()</span>. </dd> <dt class="new_code">$timing</dt> <dd> The only point in time to test the condition. The first call starts at zero. </dd> <dt class="new_code">$count</dt> <dd>The number of calls expected.</dd> </dl> The method <span class="new_code">expectMaximumCallCount()</span> is slightly different in that it will only ever generate a failure. It is silent if the limit is never reached. </p> <p> Also if you have juste one call in your test, make sure you're using <span class="new_code">expectOnce</span>.<br> Using <span class="new_code">$mocked->expectAt(0, 'method', 'args);</span> on its own will not be catched : checking the arguments and the overall call count are currently independant. </p> <p> Like the assertions within test cases, all of the expectations can take a message override as an extra parameter. Also the original failure message can be embedded in the output as "%s". </p> <p><a class="target" name="approaches"><h2>Other approaches</h2></a></p> <p> There are three approaches to creating mocks including the one that SimpleTest employs. Coding them by hand using a base class, generating them to a file and dynamically generating them on the fly. </p> <p> Mock objects generated with <a href="simple_test.html">SimpleTest</a> are dynamic. They are created at run time in memory, using <span class="new_code">eval()</span>, rather than written out to a file. This makes the mocks easy to create, a one liner, especially compared with hand crafting them in a parallel class hierarchy. The problem is that the behaviour is usually set up in the tests themselves. If the original objects change the mock versions that the tests rely on can get out of sync. This can happen with the parallel hierarchy approach as well, but is far more quickly detected. </p> <p> The solution, of course, is to add some real integration tests. You don't need very many and the convenience gained from the mocks more than outweighs the small amount of extra testing. You cannot trust code that was only tested with mocks. </p> <p> If you are still determined to build static libraries of mocks because you want to simulate very specific behaviour, you can achieve the same effect using the SimpleTest class generator. In your library file, say <em>mocks/connection.php</em> for a database connection, create a mock and inherit to override special methods or add presets...<pre><?php require_once('simpletest/mock_objects.php'); require_once('../classes/connection.php');<strong> Mock::generate('Connection', 'BasicMockConnection'); class MockConnection extends BasicMockConnection { function MockConnection() { $this->BasicMockConnection(); $this->setReturn('query', false); } }</strong>?></pre> The generate call tells the class generator to create a class called <span class="new_code">BasicMockConnection</span> rather than the usual <span class="new_code">MockConnection</span>. We then inherit from this to get our version of <span class="new_code">MockConnection</span>. By intercepting in this way we can add behaviour, here setting the default value of <span class="new_code">query()</span> to be false. By using the default name we make sure that the mock class generator will not recreate a different one when invoked elsewhere in the tests. It never creates a class if it already exists. As long as the above file is included first then all tests that generated <span class="new_code">MockConnection</span> should now be using our one instead. If we don't get the order right and the mock library creates one first then the class creation will simply fail. </p> <p> Use this trick if you find you have a lot of common mock behaviour or you are getting frequent integration problems at later stages of testing. </p> </div> References and related information... <ul><li> The original <a href="http://www.mockobjects.com/">Mock objects</a> paper. </li><li> SimpleTest project page on <a href="http://sourceforge.net/projects/simpletest/">SourceForge</a>. </li><li> SimpleTest home page on <a href="http://www.lastcraft.com/simple_test.php">LastCraft</a>. </li></ul><div class="menu_back"><div class="menu"><a href="index.html">SimpleTest</a> | <a href="overview.html">Overview</a> | <a href="unit_test_documentation.html">Unit tester</a> | <a href="group_test_documentation.html">Group tests</a> | <span class="chosen">Mock objects</span> | <a href="partial_mocks_documentation.html">Partial mocks</a> | <a href="reporter_documentation.html">Reporting</a> | <a href="expectation_documentation.html">Expectations</a> | <a href="web_tester_documentation.html">Web tester</a> | <a href="form_testing_documentation.html">Testing forms</a> | <a href="authentication_documentation.html">Authentication</a> | <a href="browser_documentation.html">Scriptable browser</a></div></div><div class="copyright"> Copyright<br>Marcus Baker 2006 </div></body></html>
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?