mock_objects_documentation.html.svn-base

来自「PHP 知识管理系统(基于树结构的知识管理系统), 英文原版的PHP源码。」· SVN-BASE 代码 · 共 758 行 · 第 1/3 页

SVN-BASE
758
字号
<html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><title>SimpleTest for PHP mock objects documentation</title><link rel="stylesheet" type="text/css" href="docs.css" title="Styles"></head><body><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><h1>Mock objects documentation</h1>        This page...        <ul><li>            <a href="#what">What are mock objects?</a>        </li><li>            <a href="#creation">Creating mock objects</a>.        </li><li>            <a href="#stub">Mocks as actors</a> or stubs.        </li><li>            <a href="#expectations">Mocks as critics</a> with expectations.        </li><li>            <a href="#approaches">Other approaches</a> including mock libraries.        </li></ul><div class="content">        <p><a class="target" name="what"><h2>What are mock objects?</h2></a></p>            <p>                Mock objects have two roles during a test case: actor and critic.            </p>            <p>                The actor behaviour is to simulate objects that are difficult to                set up or time consuming to set up for a test.                The classic example is a database connection.                Setting up a test database at the start of each test would slow                testing to a crawl and would require the installation of the                database engine and test data on the test machine.                If we can simulate the connection and return data of our                choosing we not only win on the pragmatics of testing, but can                also feed our code spurious data to see how it responds.                We can simulate databases being down or other extremes                without having to create a broken database for real.                In other words, we get greater control of the test environment.            </p>            <p>                If mock objects only behaved as actors they would simply be                known as server stubs.                This was originally a pattern named by Robert Binder (Testing                object-oriented systems: models, patterns, and tools,                Addison-Wesley) in 1999.            </p>            <p>                A server stub is a simulation of an object or component.                It should exactly replace a component in a system for test                or prototyping purposes, but remain lightweight.                This allows tests to run more quickly, or if the simulated                class has not been written, to run at all.            </p>            <p>                However, the mock objects not only play a part (by supplying chosen                return values on demand) they are also sensitive to the                messages sent to them (via expectations).                By setting expected parameters for a method call they act                as a guard that the calls upon them are made correctly.                If expectations are not met they save us the effort of                writing a failed test assertion by performing that duty on our                behalf.            </p>            <p>                In the case of an imaginary database connection they can                test that the query, say SQL, was correctly formed by                the object that is using the connection.                Set them up with fairly tight expectations and you will                hardly need manual assertions at all.            </p>                <p><a class="target" name="creation"><h2>Creating mock objects</h2></a></p>            <p>                In the same way that we create server stubs, all we need is an                existing class, say a database connection that looks like this...<pre><strong>class DatabaseConnection {    function DatabaseConnection() {    }        function query() {    }        function selectQuery() {    }}</strong></pre>                The class does not need to have been implemented yet.                To create a mock version of the class we need to include the                mock object library and run the generator...<pre><strong>require_once('simpletest/unit_tester.php');require_once('simpletest/mock_objects.php');require_once('database_connection.php');Mock::generate('DatabaseConnection');</strong></pre>                This generates a clone class called                <span class="new_code">MockDatabaseConnection</span>.                We can now create instances of the new class within                our test case...<pre>require_once('simpletest/unit_tester.php');require_once('simpletest/mock_objects.php');require_once('database_connection.php');Mock::generate('DatabaseConnection');<strong>class MyTestCase extends UnitTestCase {        function testSomething() {        $connection = &amp;new MockDatabaseConnection();    }}</strong></pre>                Unlike the generated stubs the mock constructor needs a reference                to the test case so that it can dispatch passes and failures while                checking its expectations.                This means that mock objects can only be used within test cases.                Despite this their extra power means that stubs are hardly ever used                if mocks are available.            </p>            <p>                <a class="target" name="stub"><h2>Mocks as actors</h2></a>            </p>            <p>                The mock version of a class has all the methods of the original,                so that operations like                <span class="new_code">$connection-&gt;query()</span> are still                legal.                The return value will be <span class="new_code">null</span>,                but we can change that with...<pre><strong>$connection-&gt;setReturnValue('query', 37)</strong></pre>                Now every time we call                <span class="new_code">$connection-&gt;query()</span> we get                the result of 37.                We can set the return value to anything, say a hash of                imaginary database results or a list of persistent objects.                Parameters are irrelevant here, we always get the same                values back each time once they have been set up this way.                That may not sound like a convincing replica of a                database connection, but for the half a dozen lines of                a test method it is usually all you need.            </p>            <p>                We can also add extra methods to the mock when generating it                and choose our own class name...<pre><strong>Mock::generate('DatabaseConnection', 'MyMockDatabaseConnection', array('setOptions'));</strong></pre>                Here the mock will behave as if the <span class="new_code">setOptions()</span>                existed in the original class.                This is handy if a class has used the PHP <span class="new_code">overload()</span>                mechanism to add dynamic methods.                You can create a special mock to simulate this situation.            </p>            <p>                Things aren't always that simple though.                One common problem is iterators, where constantly returning                the same value could cause an endless loop in the object                being tested.                For these we need to set up sequences of values.                Let's say we have a simple iterator that looks like this...<pre>class Iterator {    function Iterator() {    }        function next() {    }}</pre>                This is about the simplest iterator you could have.                Assuming that this iterator only returns text until it                reaches the end, when it returns false, we can simulate it                with...<pre>Mock::generate('Iterator');class IteratorTest extends UnitTestCase() {        function testASequence() {<strong>        $iterator = &amp;new MockIterator();        $iterator-&gt;setReturnValue('next', false);        $iterator-&gt;setReturnValueAt(0, 'next', 'First string');        $iterator-&gt;setReturnValueAt(1, 'next', 'Second string');</strong>        ...    }}</pre>                When <span class="new_code">next()</span> is called on the                mock iterator it will first return "First string",                on the second call "Second string" will be returned                and on any other call <span class="new_code">false</span> will                be returned.                The sequenced return values take precedence over the constant                return value.                The constant one is a kind of default if you like.            </p>            <p>                Another tricky situation is an overloaded                <span class="new_code">get()</span> operation.                An example of this is an information holder with name/value pairs.                Say we have a configuration class like...<pre>class Configuration {    function Configuration() {    }        function getValue($key) {    }}</pre>                This is a classic situation for using mock objects as                actual configuration will vary from machine to machine,                hardly helping the reliability of our tests if we use it                directly.                The problem though is that all the data comes through the                <span class="new_code">getValue()</span> method and yet                we want different results for different keys.

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?