Giter Site home page Giter Site logo

proxy-object's Introduction

=========== ProxyObject

Initiated by Thomas Weinert back in 2008 I picked up his work and completed, extended, and tested it. The outcome is this little library making it much easier to generate a proxy of your system under test (SUT). Another thought on this library was, that it should be very easy to use if you know the way to mock classes and methods in PHPUnit. Proxy-object has almost the same API, but does not change the behavior of the proxied class/method. The only purpose is to expose hidden (protected & private) methods and members.

Current travis status: Build Status

Installation

Thanks to the feedback of beberlei the source is now PSR-0 compatible. There is no specific installation routine to be followed. Just clone or checkout the source into to your project and use it. In case you don't use a PSR-0 compatible autoloader, you only have to add the bootstrap.php into your bootstrap or autoloader.

Composer

Add the following lines to your composer.json file and update your project's composer installation.

{
    "require-dev": {
        "lapistano/proxy-object": "2.*"
    }
}

This composer configuration will checkout the sources tagged as the 2nd release. In case your want the 'cutting eadge' version replace '2.*' by 'dev-master'. But be alarmed that this might be broken sometimes.

NOTE: In case you do not know what this means the composer project website is a good place to start.

Github

Thus I recommend the composer way to make proxy-object a dependency to your project. The sources are also available via github. Just clone it as you might be familiar with.

$ git clone git://github.com/lapistano/proxy-object.git

Usecases

  1. Exposing invisible Methods

One of the main purpose of this library is to expose invisible (private or protected) methods to the SUT. To do so use just create a new ProxyBuilder object and pass the method to be exposed.

$proxy = new \lapistano\ProxyObject\ProxyBuilder('myClass');

// generate and configure proxied object
$proxiedObject = $proxy
    ->setConstructorArgs(array('Argument1', 'Argument2'))
    ->setMethods(array('myMethod'))
    ->getProxy();

// invoke proxied method
$proxiedObject->myMethod();
  1. Exposing invisible Members

Another purpose of this library is to expose invisible members not reachable via a setter. This is to prevent you from writing setter methods just for the purpose of unit testing. Use the setProperties() method to archieve.

$proxy = new \lapistano\ProxyObject\ProxyBuilder('myClass');

// generate and configure proxied object
$proxiedObject = $proxy
    ->setProperties(array('myMember'))
    ->getProxy();

// change content proxied member
$proxiedObject->myMember = 'another value';

Despite the fact that it is possible to expose private members by naming them in the setProperties array, generating a proxy object without the property declaration will only expose protected members. This is because I am not a big fan of exposing too much from a class. If someone thinks this should be changed, I would be more than happy to discuss this topic.

  1. Creating a proxied object without calling the constructor

Sometimes it is necessary to supress the invokation of the defined constructor. Therefore I followed the API of PHPunits MockBuilder and added the disableOriginalConstructor() method.

$proxy = new \lapistano\ProxyObject\ProxyBuilder('myClass');

// generate and configure proxied object
$proxiedObject = $proxy
    ->disableOriginalConstructor()
    ->getProxy();

// change value of proxied member
$proxiedObject->myMember = 'another value';

Ease access to the proxy-object in your test suite

Since I am really lazy ;) and I really like convenience I extended the PHPUnit_Framework_TestCase class and added the following method.

/**
 * Provides a ProxyBuilder object.
 *
 * @param string $classname
 * @return lapistano\ProxyObject\ProxyBuilder
 */
protected function getProxyBuilder($classname) {
    return new \lapistano\ProxyObject\ProxyBuilder($classname);
}

Every of your test cases should now extend your own extended test case class so you can create a new proxy builder by just calling $this->getProxyBuilder('\\my\\namespace\\myclass');. Used in one of the examples above it will look like this.

// generate and configure proxied object
$proxiedObject = $this->getProxyBuilder('myClass')
    ->disableOriginalConstructor()
    ->getProxy();

// change value of proxied member
$proxiedObject->myMember = 'another value';

Documentation

Since there is a exhausting documentation of the API in the source code, I decided not to write a separate one. Use phpDocumentor to extract and generate your own documentation. I added a phpdoc.example.ini in the doc/config folder. Follow the instructions in the doc/config/README to setup the generation of the documentation.

Limitations

As you might expect there are also some limitations this library has to deal with. This limitations are not introduced by this implementation, but are limitations which come from PHP. So it is not possible to expose methods marked as final or static.

Future stuff

  • Improve error messages (e.g. by telling why a method/member could not be exposed)

proxy-object's People

Contributors

bashofmann avatar beberlei avatar gjerokrsteski avatar hairmare avatar lapistano avatar micha149 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

proxy-object's Issues

Access to private methods

Hi Bastian,

first of all, thanks for your interesing presentation at the PHP UG Cologne last week.
Regarding the user group's discussion that night I'd like to share my point of view, too.

Short version: yes, if it's possible there should certainly be a way to make private methods accessible.

There are at least two important scenarios where this makes sense:

  1. CCN reduction
    As a use case grows, i.e. more and more scenarios are added, the number of independent execution paths usually grows, too. This is also known as the CCN (cyclomatic complexity number, http://en.wikipedia.org/wiki/Cyclomatic_Complexity_Number): Each if, switch, etc in your code increases complexity.
    Theoretically, it's OK to have a CCN of 100 - as long as you are willing to write (and maintain) 100 Test Cases for that one single function :-).

However, in the real world noone would want that, so this kind of thing is considered code smell anti-pattern "long method". A common solution for that problem is refactoring, namely refactoring pattern "extract method".
As a result the once big function will be split up into several small ones, all of which will preferably have a handy CCN of < 10. Then testing will be possible and fun again.
And as there is usually no reason to make these small functions more visible than private a testing helper like proxy-object would be really helpful if it supported private methods, too.

  1. Comments
    Another reason to extract small methods can once again be found in Martin Fowler's classic book on refactoring:
    "When you feel the need to write a comment, first try to refactor the code so that any comment becomes superflouus."

So we often extract small - and mostly private methods - with verbose names to achieve this.

Thanks again for your cool tool!
Looking forward to seeing in Cologne soon.

Andreas Czakaj
IT Director
Pixelpark AG

Generator::getInstance() fails if the value of the reflected method argument is a not instantiable class

getInstance() fails if class is not instaniable (\ReflectionCalss::isInstantiable()).

Example:
// class to be proxied
namespace {
class DummyWithConstructorAndInterfaceTypeHint
{
public function __construct(\Countable $items)
{
return;
}}}

// proxy generation
$proxy = new \lapistano\ProxyObject\ProxyObject();
$dummy = $proxy->getProxyBuilder('\DummyWithConstructorAndInterfaceTypeHint')
->getProxy();

Expected:
$dummy contains an instance of DummyWithConstructorAndInterfaceTypeHint

Current:
PHP Fatal error: Cannot instantiate interface Countable in /home/lapistano/development/github/proxy-object/src/lapistano/ProxyObject/Generator.php on line 373

Abstract classes?

How can I test an abstract class? I was hoping it would just work using this.

$myObj = new \lapistano\ProxyObject\ProxyBuilder('MyAbstractClass');
$result = $myObj->myProtectedMethodUnderTest();

but I get an error like this:

PHP Fatal error: Class Proxy_MyAbstractClass_f6efbaef contains 2 abstract methods and must therefore be declared abstract or implement the remaining methods

Is this something this library can be used for?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.