Development

Changeset 19547

You must first sign up to be able to contribute.

Changeset 19547

Show
Ignore:
Timestamp:
06/25/09 16:43:04 (8 months ago)
Author:
bschussek
Message:

Wrote an extensive documentation

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • plugins/sfLimeExtraPlugin/trunk/README

    r19528 r19547  
    1 This plugin contains a grid subframework that allows to read various data sources in a unified way. These data sources can be rendered in a  
    2 customizable grid with a custom formatter. The code of this plugin will eventually become bundled with symfony 1.3. 
    3  
    4 Currently, arrays and Doctrine tables can be used as data sources. Propel, XML and CSV are planned for the final version Only a HTML formatter is  
    5 currently supported. A raw text formatter and an XML formatter are planned for the final version. 
    6  
    7 THIS PLUGIN IS IN VERY EARLY ALPHA STAGES AND IS NOT RECOMMENDED FOR PRODUCTION USE. 
    8  
    9 LICENSE 
     1Creating Stubs and Mocks with sfLimeExtraPlugin 
     2=============================================== 
     3 
     4Stubs and mock objects can simulate the behaviour of real classes. They are 
     5useful for testing code that depends on third classes (the "dependent-on  
     6component"). For the purpose of the test it is mostly sufficient though to  
     7provide a fake object that behaves as if it was the real class without ever  
     8executing the real class's code. 
     9 
     10sfLimeExtraPlugin provides a light-weight and intuitive mocking framework for 
     11use with **lime**. 
     12 
     13**Attention: This plugin is still in its alpha stages and subject to change. 
     14I would be happy if you want to try it out to give me feedback.** 
     15 
     16 
     17Installation 
     18------------ 
     19 
     20The plugin can be obtained by checking it out from SVN: 
     21 
     22    svn co http://svn.symfony-project.com/plugins/sfLimeExtraPlugin/trunk 
     23   
     24As soon as the plugin is installed, it is ready to use. 
     25 
     26 
     27Stubs 
     28----- 
     29 
     30Stubs are objects that offer the same methods as the stubbed class but usually 
     31return fixed values for the purpose of a test. Let's start with the following 
     32class as example: 
     33 
     34    [php] 
     35    class User 
     36    { 
     37      protected $storage; 
     38       
     39      public function User(SessionStorage $storage) 
     40      { 
     41        $this->storage = $storage; 
     42      } 
     43       
     44      public function setAttribute($name, $value) 
     45      { 
     46        $this->storage->write($name, $value); 
     47        $this->storage->close(); 
     48      } 
     49       
     50      public function getAttribute($name) 
     51      { 
     52        return $this->storage->read($name); 
     53      } 
     54    } 
     55     
     56Let's look at a unit test for ``setAttribute()``: 
     57 
     58    [php] 
     59    $t->comment('Attributes can be read from the session'); 
     60     
     61      // fixture 
     62      $stub = new SessionStorageDatabase(); 
     63      $stub->write('Foo', 'Bar'); 
     64      $stub->close(); 
     65      $u = new User($stub); 
     66      // test 
     67      $value = $u->getAttribute('Foo'); 
     68      // assertions 
     69      $t->is($value, 'Bar', 'The value has been read'); 
     70 
     71Now ``SessionStorage`` might require access to the file system or access to a  
     72database, as in our case. Accessing a database everytime you test your ``User``  
     73class will heavily affect the speed of your tests. Thus the option is to replace  
     74``SessionStorage`` with a fake implementation (a stub). 
     75 
     76    [php] 
     77    class StubSessionStorage implements SessionStorage 
     78    { 
     79      private $name; 
     80      private $value; 
     81       
     82      public function __construct($name, $value) 
     83      { 
     84        $this->name = $name; 
     85        $this->value = $value; 
     86      } 
     87       
     88      public function read($name) 
     89      { 
     90        return $name == $this->name ? $value : null; 
     91      } 
     92     
     93      public function write($name, $value) {} 
     94      public function close() {} 
     95    } 
     96     
     97Let's adapt our test to use the ``StubSessionStorage``: 
     98 
     99    [php] 
     100    $t->comment('Attributes can be read from the session'); 
     101     
     102      // fixture 
     103      $u = new User(new StubSessionStorage('Foo', 'Bar')); 
     104      // test 
     105      $value = $u->getAttribute('Foo'); 
     106      // assertions 
     107      $t->is($value, 'Bar', 'The value has been read'); 
     108       
     109As you can see, our test just got a lot easier to read. The drawback is that 
     110we had to invest a lot of code into writing our stub class. Usually it is 
     111worth the effort though, because the stub class can be reused in other tests. 
     112Especially in the long term, a good test readability and maintainability pays 
     113off. 
     114 
     115Note how we injected the name and the value of the expected parameter into 
     116our stub class. This means again more code in the stub class, but in the test 
     117it is very clear why ``getAttribute()`` should return ``'Bar'`` when called 
     118with the argument ``'Foo'``. 
     119 
     120sfLimeExtraPlugin provides you with facilities to create stubs a lot easier 
     121than by having to write your own stub classes. With sfLimeExtraPlugin, we can 
     122change the test above code to: 
     123 
     124    [php] 
     125    $t->comment('Attributes can be read from the session'); 
     126     
     127      // fixture 
     128      $stub = lime_mock::create('SessionStorage'); 
     129      $stub->read('Foo')->returns('Bar'); 
     130      $stub->replay(); 
     131      $u = new User($stub); 
     132      // test 
     133      $value = $u->getAttribute('Foo'); 
     134      // assertions 
     135      $t->is($value, 'Bar', 'The value has been read'); 
     136       
     137As you can see, we just create the stub by calling ``lime_mock::create()`` with 
     138the stubbed class or interface name. Then we "record" the methods that should 
     139be called with what parameters and their return values. In the end we switch 
     140the storage to "replay" mode. In this mode, the methods that we just configured 
     141will return the desired return values, when called with the right arguments. 
     142 
     143 
     144Mock Objects 
     145------------ 
     146 
     147Sometimes it is not enough to replace a dependent-on component by setting up 
     148static method return values. In some cases you will also want to test whether 
     149the dependent-on component receives the right data and method calls. 
     150 
     151For this purpose we will extend our last example a bit. We don't only want to 
     152test whether the user can read attributes from the session, but we also want to 
     153test whether attributes are written correctly *into* the session. 
     154 
     155    [php] 
     156    $t->comment('Attributes can be stored permanently'); 
     157     
     158      // fixture 
     159      $mock = lime_mock::create('SessionStorage', $t); 
     160      $mock->write('Foo', 'Bar'); 
     161      $mock->close(); 
     162      $mock->replay(); 
     163      $u = new User($mock); 
     164      // test 
     165      $u->setAttribute('Foo', 'Bar'); 
     166      // assertions 
     167      $mock->verify(); 
     168       
     169In this example, the new concept of *verification* comes into play. After 
     170executing your test code you can call ``verify()`` on the mock object to verify 
     171whether all the expected methods have been called with the right parameters. 
     172If any of the methods would not have been called, ``verify()`` would result 
     173in a failed test. 
     174 
     175 
     176Modifiers 
     177========= 
     178 
     179This section covers the capabilities of the mock objects. It describes which 
     180methods you have to call to configure the mock for your needs. 
     181 
     182 
     183``returns()`` 
     184------------- 
     185 
     186Configures a method to return a specific value. 
     187 
     188    [php] 
     189    $mock->doSomething()->returns('Foobar'); 
     190    $mock->replay(); 
     191     
     192    echo $mock->doSomething(); // prints 'Foobar' 
     193     
     194     
     195``throws()`` 
     196------------ 
     197 
     198Configures a method to throw a specific exception. 
     199 
     200    [php] 
     201    $mock->doSomething()->throws('InvalidArgumentException'); 
     202    $mock->replay(); 
     203     
     204    $mock->doSomething(); // throws an InvalidArgumentException 
     205     
     206     
     207``times()`` 
     208----------- 
     209 
     210Configures a method to be called a specific number of times. 
     211 
     212    [php] 
     213    $mock->doSomething()->times(2); 
     214    $mock->replay(); 
     215     
     216    $mock->doSomething(); 
     217    $mock->verify(); // results in a failed test 
     218     
     219     
     220``setFailOnVerify()`` 
     221--------------------- 
     222     
     223If an unexpected method is called or if a method is called to often, a  
     224``lime_expectation_exception`` is thrown. 
     225 
     226    [php] 
     227    $mock->doSomething()->times(2); 
     228    $mock->replay(); 
     229     
     230    $mock->doSomething(); 
     231    $mock->doSomething(); 
     232    $mock->doSomething(); // throws a lime_expectation_exception 
     233     
     234This is very useful for discovering where the unexpected call resulted from. You 
     235can suppress this behaviour though by calling ``setFailOnVerify()``, so that 
     236your code will only be validated when ``verify()`` is called. 
     237 
     238    [php] 
     239    $mock->doSomething()->times(2); 
     240    $mock->replay(); 
     241     
     242    $mock->doSomething(); 
     243    $mock->doSomething(); 
     244    $mock->doSomething(); 
     245    $mock->verify(); // results in a failed test 
     246     
     247     
     248``setExpectNothing()`` 
     249---------------------- 
     250 
     251By default, all method calls are ignored if you did not set up any expected 
     252methods. 
     253 
     254    [php] 
     255    $mock->replay(); 
     256    $mock->doSomething(); // ignored 
     257    $mock->verify(); // results in a passed test 
     258     
     259You can configure the mock though to verify that exactly no method has been 
     260called: 
     261 
     262    [php] 
     263    $mock->setExpectNothing(); 
     264    $mock->doSomething(); // throws a lime_expectation_exception 
     265     
     266     
     267Mocking A Modifier 
     268------------------ 
     269 
     270For each mock object, the following methods are generated automatically: 
     271 
     272* ``verify()`` 
     273* ``replay()`` 
     274* ``setExpectNothing`` 
     275* ``setStrict()`` 
     276* ``setFailOnVerify()`` 
     277 
     278These methods allow for a very comfortable usage. The drawback is that you 
     279cannot mock methods with the same name in your class. 
     280 
     281    [php] 
     282    $mock = lime_mock::create('MusicPlayer', $t); 
     283    $mock->replay()->returns('...'); // does not work 
     284     
     285The solution is, to set the third parameter ``$generateControls`` to ``false`` 
     286when calling ``create()``. When you do that, you will have to call the above 
     287methods statically in ``lime_mock`` with the mock object as first argument. 
     288 
     289    [php] 
     290    $mock = lime_mock::create('MusicPlayer', $t, false); 
     291    $mock->replay()->returns('Foobar'); 
     292    lime_mock::replay($mock); 
     293     
     294    echo $mock->replay(); // prints 'Foobar' 
     295     
     296    lime_mock::verify($mock); // results in a successful test 
     297     
     298 
     299License 
    10300------- 
    11301 

The Sensio Labs Network

Since 1998, Sensio Labs has been promoting the Open-Source software movement by providing quality web application development, training, consulting.
Sensio Labs also supports several large Open-Source projects.