partial_mocks_documentation.html.svn-base

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

SVN-BASE
446
字号
<html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><title>SimpleTest for PHP partial mocks 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>                |                <a href="mock_objects_documentation.html">Mock objects</a>                |                <span class="chosen">Partial mocks</span>                |                <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>Partial mock objects documentation</h1>        This page...        <ul><li>            <a href="#inject">The mock injection problem</a>.        </li><li>            Moving creation to a <a href="#creation">protected factory</a> method.        </li><li>            <a href="#partial">Partial mocks</a> generate subclasses.        </li><li>            Partial mocks <a href="#less">test less than a class</a>.        </li></ul><div class="content">                    <p>                A partial mock is simply a pattern to alleviate a specific problem                in testing with mock objects,                that of getting mock objects into tight corners.                It's quite a limited tool and possibly not even a good idea.                It is included with SimpleTest because I have found it useful                on more than one occasion and has saved a lot of work at that point.            </p>                <p><a class="target" name="inject"><h2>The mock injection problem</h2></a></p>            <p>                When one object uses another it is very simple to just pass a mock                version in already set up with its expectations.                Things are rather tricker if one object creates another and the                creator is the one you want to test.                This means that the created object should be mocked, but we can                hardly tell our class under test to create a mock instead.                The tested class doesn't even know it is running inside a test                after all.            </p>            <p>                For example, suppose we are building a telnet client and it                needs to create a network socket to pass its messages.                The connection method might look something like...<pre><strong>&lt;?phprequire_once('socket.php');    class Telnet {    ...    function &amp;connect($ip, $port, $username, $password) {        $socket = &amp;new Socket($ip, $port);        $socket-&gt;read( ... );        ...    }}?&gt;</strong></pre>                We would really like to have a mock object version of the socket                here, what can we do?            </p>            <p>                The first solution is to pass the socket in as a parameter,                forcing the creation up a level.                Having the client handle this is actually a very good approach                if you can manage it and should lead to factoring the creation from                the doing.                In fact, this is one way in which testing with mock objects actually                forces you to code more tightly focused solutions.                They improve your programming.            </p>            <p>                Here this would be...<pre>&lt;?phprequire_once('socket.php');    class Telnet {    ...    <strong>function &amp;connect(&amp;$socket, $username, $password) {        $socket-&gt;read( ... );        ...    }</strong>}?&gt;</pre>                This means that the test code is typical for a test involving                mock objects.<pre>class TelnetTest extends UnitTestCase {    ...    function testConnection() {<strong>        $socket = &amp;new MockSocket($this);        ...        $telnet = &amp;new Telnet();        $telnet-&gt;connect($socket, 'Me', 'Secret');        ...</strong>    }}</pre>                It is pretty obvious though that one level is all you can go.                You would hardly want your top level application creating                every low level file, socket and database connection ever                needed.                It wouldn't know the constructor parameters anyway.            </p>            <p>                The next simplest compromise is to have the created object passed                in as an optional parameter...<pre>&lt;?phprequire_once('socket.php');    class Telnet {    ...<strong>    function &amp;connect($ip, $port, $username, $password, $socket = false) {        if (!$socket) {            $socket = &amp;new Socket($ip, $port);        }        $socket-&gt;read( ... );</strong>        ...        return $socket;    }}?&gt;</pre>                For a quick solution this is usually good enough.                The test now looks almost the same as if the parameter                was formally passed...<pre>class TelnetTest extends UnitTestCase {    ...    function testConnection() {<strong>        $socket = &amp;new MockSocket($this);        ...        $telnet = &amp;new Telnet();        $telnet-&gt;connect('127.0.0.1', 21, 'Me', 'Secret', &amp;$socket);        ...</strong>    }}</pre>                The problem with this approach is its untidiness.                There is test code in the main class and parameters passed                in the test case that are never used.                This is a quick and dirty approach, but nevertheless effective                in most situations.            </p>            <p>                The next method is to pass in a factory object to do the creation...<pre>&lt;?phprequire_once('socket.php');    class Telnet {<strong>   function Telnet(&amp;$network) {        $this-&gt;_network = &amp;$network;    }</strong>    ...    function &amp;connect($ip, $port, $username, $password) {<strong>        $socket = &amp;$this-&gt;_network-&gt;createSocket($ip, $port);        $socket-&gt;read( ... );</strong>        ...        return $socket;    }}?&gt;</pre>                This is probably the most highly factored answer as creation                is now moved into a small specialist class.                The networking factory can now be tested separately, but mocked                easily when we are testing the telnet class...<pre>class TelnetTest extends UnitTestCase {    ...    function testConnection() {<strong>        $socket = &amp;new MockSocket($this);        ...        $network = &amp;new MockNetwork($this);        $network-&gt;setReturnReference('createSocket', $socket);        $telnet = &amp;new Telnet($network);        $telnet-&gt;connect('127.0.0.1', 21, 'Me', 'Secret');        ...</strong>    }}</pre>                The downside is that we are adding a lot more classes to the                library.                Also we are passing a lot of factories around which will                make the code a little less intuitive.                The most flexible solution, but the most complex.            </p>            <p>                Is there a middle ground?

⌨️ 快捷键说明

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